# OpenWeatherMap daily forecasts with free API using plain OpenHAB rules

**URL:** <https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513>\
**Category:** Tutorials & Examples\
**Tags:** rules, openhab2, openweathermap\
**Created:** [February 22, 2019, 3:47pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513 "2019-02-22T15:47:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [February 22, 2019, 3:47pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/1 "2019-02-22T15:47:17Z")

</div>

In case you want to generate daily forecasts with the data provided by a free OpenWeatherMap account **without relying on Jython** , here’s what I did.

I create a number of proxy items that will contain the computed forecast values. These values are updated whenever a new forecast is provided by OWM or at midnight (to overcome the today → tomorrow case for forecasts).

The code below provides the following forecast data per day for up to 3 days in advance:

1. Rain (total daily forecast)
2. Snow (total daily forecast)
3. Temperature (low / high)
4. Max wind speed
5. Pressure (low / high)
6. Humidity (low / high)

 ![IMG_7168](https://community-openhab-org.s3.dualstack.eu-central-1.amazonaws.com/original/3X/e/9/e95e6bbf3caff5625ed85350782fe0b123a1d9dc.png) ![IMG_7169](https://community-openhab-org.s3.dualstack.eu-central-1.amazonaws.com/original/3X/7/6/76c10f0a3167d5680154320f737e3a08579a6b7b.png)

The data are computed by assigning forecast data to the proper day. Forecast values for the current day are computed, taking into account the current measurement values.

Define the following OWM items:

```csv
DateTime Weather_OWM_Current_ObservationTime "Observation time [%1$tY-%1$tm-%1$td]" 
 (gWeatherCurrent) {
	channel="openweathermap:weather-and-forecast:c06828c6:local:current#time-stamp"
}

// The following Items will store the computed daily forecast values

Number:Length Weather_OWM_Forecast_Rain_Day0 "Rain Total [%.1f mm]" <rain>
Number:Length Weather_OWM_Forecast_Rain_Day1 "Rain Total [%.1f mm]" <rain>
Number:Length Weather_OWM_Forecast_Rain_Day2 "Rain Total [%.1f mm]" <rain>
Number:Length Weather_OWM_Forecast_Rain_Day3 "Rain Total [%.1f mm]" <rain>

Number:Length Weather_OWM_Forecast_Snow_Day0 "Snow Total [%.1f mm]" <snow>
Number:Length Weather_OWM_Forecast_Snow_Day1 "Snow Total [%.1f mm]" <snow>
Number:Length Weather_OWM_Forecast_Snow_Day2 "Snow Total [%.1f mm]" <snow>
Number:Length Weather_OWM_Forecast_Snow_Day3 "Snow Total [%.1f mm]" <snow>

Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day0 "Temperature Low [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day1 "Temperature Low [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day2 "Temperature Low [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day3 "Temperature Low [%.1f °C]" <temperature>

Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day0 "Temperature High [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day1 "Temperature High [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day2 "Temperature High [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day3 "Temperature High [%.1f °C]" <temperature>

Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day0 "Max Wind Speed [%.0f km/h]" <wind>
Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day1 "Max Wind Speed [%.0f km/h]" <wind>
Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day2 "Max Wind Speed [%.0f km/h]" <wind>
Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day3 "Max Wind Speed [%.0f km/h]" <wind>

Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day0 "Pressure Low [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day1 "Pressure Low [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day2 "Pressure Low [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day3 "Pressure Low [%.1f hPa]" <pressure>

Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day0 "Pressure High [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day1 "Pressure High [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day2 "Pressure High [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day3 "Pressure High [%.1f hPa]" <pressure>

Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day0 "Humidity Low [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day1 "Humidity Low [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day2 "Humidity Low [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day3 "Humidity Low [%d %unit%]" <humidity>

Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day0 "Humidity High [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day1 "Humidity High [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day2 "Humidity High [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day3 "Humidity High [%d %unit%]" <humidity>

```

In your sitemap:

```csv
Text item=Weather_OWM_Forecast_Time_72h label="Forecast (3 days) [until %1$tA]" icon="sun_clouds" {
	Frame item=Weather_OWM_Current_ObservationTime label="Today [%1$tA, %1$tb %1$td]" icon="calendar" {
		Default item=Weather_OWM_Forecast_Temperature_Min_Day0 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Temperature_Max_Day0 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Rain_Day0
		Default item=Weather_OWM_Forecast_Snow_Day0
		Default item=Weather_OWM_Forecast_Wind_Speed_Max_Day0
		Default item=Weather_OWM_Forecast_Humidity_Min_Day0
		Default item=Weather_OWM_Forecast_Humidity_Max_Day0
		Default item=Weather_OWM_Forecast_Pressure_Min_Day0
		Default item=Weather_OWM_Forecast_Pressure_Max_Day0
	}
	
	Frame item=Weather_OWM_Forecast_Time_24h label="Tomorrow [%1$tA, %1$tb %1$td]" icon="calendar" {
		Default item=Weather_OWM_Forecast_Temperature_Min_Day1 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Temperature_Max_Day1 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Rain_Day1
		Default item=Weather_OWM_Forecast_Snow_Day1
		Default item=Weather_OWM_Forecast_Wind_Speed_Max_Day1
		Default item=Weather_OWM_Forecast_Humidity_Min_Day1
		Default item=Weather_OWM_Forecast_Humidity_Max_Day1
		Default item=Weather_OWM_Forecast_Pressure_Min_Day1
		Default item=Weather_OWM_Forecast_Pressure_Max_Day1
	}

	Frame item=Weather_OWM_Forecast_Time_48h label="In 2 days [%1$tA, %1$tb %1$td]" icon="calendar" {
		Default item=Weather_OWM_Forecast_Temperature_Min_Day2 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Temperature_Max_Day2 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Rain_Day2
		Default item=Weather_OWM_Forecast_Snow_Day2
		Default item=Weather_OWM_Forecast_Wind_Speed_Max_Day2
		Default item=Weather_OWM_Forecast_Humidity_Min_Day2
		Default item=Weather_OWM_Forecast_Humidity_Max_Day2
		Default item=Weather_OWM_Forecast_Pressure_Min_Day2
		Default item=Weather_OWM_Forecast_Pressure_Max_Day2
	}

	Frame item=Weather_OWM_Forecast_Time_72h label="In 3 days [%1$tA, %1$tb %1$td]" icon="calendar" {
		Default item=Weather_OWM_Forecast_Temperature_Min_Day3 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Temperature_Max_Day3 valuecolor=[
			>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
		]
		Default item=Weather_OWM_Forecast_Rain_Day3
		Default item=Weather_OWM_Forecast_Snow_Day3
		Default item=Weather_OWM_Forecast_Wind_Speed_Max_Day3
		Default item=Weather_OWM_Forecast_Humidity_Min_Day3
		Default item=Weather_OWM_Forecast_Humidity_Max_Day3
		Default item=Weather_OWM_Forecast_Pressure_Min_Day3
		Default item=Weather_OWM_Forecast_Pressure_Max_Day3
	}
}

```

Now comes the (rather verbose) rule that takes care of computing the forecasts for daily rain and snow, and daily low and high temperatures:

```csv
rule "Update forecast data"
when
    // Time cron "0/15 * * * * ?" or // Only for debug purposes: display results every 15 seconds
    Time cron "0 0 0 * * ?" or
    Item Weather_OWM_Forecast_Time_03h changed
then
	val String ruleTitle = "UpdateOWMForecastInfo"

    val DateTime dtNow = now
    val Number now_d = dtNow.getDayOfMonth
    val Number now_m = dtNow.getMonthOfYear
    val Number now_h = dtNow.getHourOfDay

    val DateTime dtForecast_03h = (new DateTime(Weather_OWM_Forecast_Time_03h.state.toString)).toDateTime
    val Number forecast_03h_d = dtForecast_03h.getDayOfMonth
    val Number forecast_03h_m = dtForecast_03h.getMonthOfYear
    val Number forecast_03h_h = dtForecast_03h.getHourOfDay
    
    logInfo(ruleTitle, "Update forecast info - today is {}/{} - 03h forecast is for {}/{} at {}h", now_d, now_m, forecast_03h_d, forecast_03h_m, forecast_03h_h)

    var Number forecastDayOffset = 0
    if (forecast_03h_d > now_d) { // No forecast for today
        logInfo(ruleTitle, "No forecast for today ({}/{} - at or after {}h", now_d, now_m, now_h)
        forecastDayOffset = 1
    }

    var Number i = 3

    var Number rain_day_0 = 0|mm
    var Number snow_day_0 = 0|mm
    var Number wind_speed_max_day_0 = 0|"km/h"
    var Number temp_min_day_0 = 100|°C // Make it a very high (!) temperature so it gets updated instantly
    var Number temp_max_day_0 = -100|°C // Make it a very low (!) temperature so it gets updated instantly
    var Number humidity_min_day_0 = 100 // Make it a 100% so it gets updated instantly
    var Number humidity_max_day_0 = 0 // Make it 0% so it gets updated instantly
    var Number pressure_min_day_0 = 2000|"hPa" // Make it very high (!) so it gets updated instantly
    var Number pressure_max_day_0 = 0|"hPa" // Make it 0 so it gets updated instantly

    if (forecastDayOffset == 0) { // Forecast data still today: set min and max to current values as a starting point
        temp_min_day_0 = Weather_OWM_Current_Temperature.state as Number
        temp_max_day_0 = temp_min_day_0
        humidity_min_day_0 = Weather_OWM_Current_Humidity.state as Number
        humidity_max_day_0 = humidity_min_day_0
        pressure_min_day_0 = Weather_OWM_Current_Pressure.state as Number
        pressure_max_day_0 = pressure_min_day_0
    }

    while ( forecast_03h_h + i - 3 <= 24 ) {
        // Rain: total
        val String rainItemName = "Weather_OWM_Forecast_Rain_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number rain = ScriptServiceUtil.getItemRegistry.getItem(rainItemName).state as Number
        rain_day_0 = rain_day_0 + rain
        // Snow: total
        val String snowItemName = "Weather_OWM_Forecast_Snow_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number snow = ScriptServiceUtil.getItemRegistry.getItem(snowItemName).state as Number
        snow_day_0 = snow_day_0 + snow
        // Wind speed: max
        val String windSpeedItemName = "Weather_OWM_Forecast_Wind_Speed_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number wind_speed = ScriptServiceUtil.getItemRegistry.getItem(windSpeedItemName).state as Number
        if (wind_speed_max_day_0 < wind_speed) wind_speed_max_day_0 = wind_speed
        // Temperature: min, max
        val String tempItemName = "Weather_OWM_Forecast_Temperature_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number temp = ScriptServiceUtil.getItemRegistry.getItem(tempItemName).state as Number
        if (temp_max_day_0 < temp) temp_max_day_0 = temp
        if (temp_min_day_0 > temp) temp_min_day_0 = temp
        // Humidity: min, max
        val String humidityItemName = "Weather_OWM_Forecast_Humidity_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number humidity = ScriptServiceUtil.getItemRegistry.getItem(humidityItemName).state as Number
        if (humidity_max_day_0 < humidity) humidity_max_day_0 = humidity
        if (humidity_min_day_0 > humidity) humidity_min_day_0 = humidity
        // Pressure: min, max
        val String pressureItemName = "Weather_OWM_Forecast_Pressure_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number pressure = ScriptServiceUtil.getItemRegistry.getItem(pressureItemName).state as Number
        if (pressure_max_day_0 < pressure) pressure_max_day_0 = pressure
        if (pressure_min_day_0 > pressure) pressure_min_day_0 = pressure

        logDebug(ruleTitle,
            "Weather forecast for 'today + {} day(s)' at {}h ({}h + {}h) -- {} = {}, rain: {}, snow: {}, wind speed: {}, pressure: {}, humidity: {}",
            forecastDayOffset, forecast_03h_h + i - 3, forecast_03h_h, i - 3,
            tempItemName, temp, rain, snow, wind_speed, pressure, humidity)
        i = i + 3
    }
    logInfo(ruleTitle,
        "Forecast in {} days: temperature: {} - {}, rain: {}, snow: {}, wind speed: {}, pressure: {} - {}, humidity: {} - {}",
        0 + forecastDayOffset,
        temp_min_day_0, temp_max_day_0, rain_day_0, snow_day_0, wind_speed_max_day_0, 
        pressure_min_day_0, pressure_max_day_0, humidity_min_day_0, humidity_max_day_0)

    var Number rain_day_1 = 0|mm
    var Number snow_day_1 = 0|mm
    var Number wind_speed_max_day_1 = 0|"km/h"
    var Number temp_min_day_1 = 100|°C // Make it a very high (!) temperature so it gets updated instantly
    var Number temp_max_day_1 = -100|°C // Make it a very low (!) temperature so it gets updated instantly
    var Number humidity_min_day_1 = 100 // Make it a 100% so it gets updated instantly
    var Number humidity_max_day_1 = 0 // Make it 0% so it gets updated instantly
    var Number pressure_min_day_1 = 2000|"hPa" // Make it a 100% so it gets updated instantly
    var Number pressure_max_day_1 = 0|"hPa" // Make it 0% so it gets updated instantly

    while ( forecast_03h_h + i - 3 <= 48 ) {
        // Rain: total
        val String rainItemName = "Weather_OWM_Forecast_Rain_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number rain = ScriptServiceUtil.getItemRegistry.getItem(rainItemName).state as Number
        rain_day_1 = rain_day_1 + rain
        // Snow: total
        val String snowItemName = "Weather_OWM_Forecast_Snow_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number snow = ScriptServiceUtil.getItemRegistry.getItem(snowItemName).state as Number
        snow_day_1 = snow_day_1 + snow
        // Wind speed: max
        val String windSpeedItemName = "Weather_OWM_Forecast_Wind_Speed_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number wind_speed = ScriptServiceUtil.getItemRegistry.getItem(windSpeedItemName).state as Number
        if (wind_speed_max_day_1 < wind_speed) wind_speed_max_day_1 = wind_speed
        // Temperature: min, max
        val String tempItemName = "Weather_OWM_Forecast_Temperature_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number temp = ScriptServiceUtil.getItemRegistry.getItem(tempItemName).state as Number
        if (temp_max_day_1 < temp) temp_max_day_1 = temp
        if (temp_min_day_1 > temp) temp_min_day_1 = temp
        // Humidity: min, max
        val String humidityItemName = "Weather_OWM_Forecast_Humidity_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number humidity = ScriptServiceUtil.getItemRegistry.getItem(humidityItemName).state as Number
        if (humidity_max_day_1 < humidity) humidity_max_day_1 = humidity
        if (humidity_min_day_1 > humidity) humidity_min_day_1 = humidity
        // Pressure: min, max
        val String pressureItemName = "Weather_OWM_Forecast_Pressure_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number pressure = ScriptServiceUtil.getItemRegistry.getItem(pressureItemName).state as Number
        if (pressure_max_day_1 < pressure) pressure_max_day_1 = pressure
        if (pressure_min_day_1 > pressure) pressure_min_day_1 = pressure

        logDebug(ruleTitle,
            "Weather forecast for 'today + {} day(s)' at {}h ({}h + {}h) -- {} = {}, rain: {}, snow: {}, wind speed: {}, pressure: {}, humidity: {}",
            1 + forecastDayOffset, forecast_03h_h + i - 3 - 24, forecast_03h_h, i - 3 - 24,
            tempItemName, temp, rain, snow, wind_speed, pressure, humidity)
        i = i + 3
    }
    logInfo(ruleTitle,
        "Forecast in {} days: temperature: {} - {}, rain: {}, snow: {}, wind speed: {}, pressure: {} - {}, humidity: {} - {}",
        1 + forecastDayOffset,
        temp_min_day_1, temp_max_day_1, rain_day_1, snow_day_1, wind_speed_max_day_1,
        pressure_min_day_1, pressure_max_day_1, humidity_min_day_1, humidity_max_day_1)

    var Number rain_day_2 = 0|mm
    var Number snow_day_2 = 0|mm
    var Number wind_speed_max_day_2 = 0|"km/h"
    var Number temp_min_day_2 = 100|°C // Make it a very high (!) temperature so it gets updated instantly
    var Number temp_max_day_2 = -100|°C // Make it a very low (!) temperature so it gets updated instantly
    var Number humidity_min_day_2 = 100 // Make it a 100% so it gets updated instantly
    var Number humidity_max_day_2 = 0 // Make it 0% so it gets updated instantly
    var Number pressure_min_day_2 = 2000|"hPa" // Make it a 100% so it gets updated instantly
    var Number pressure_max_day_2 = 0|"hPa" // Make it 0% so it gets updated instantly

    while ( forecast_03h_h + i - 3 <= 72 ) {
        // Rain: total
        val String rainItemName = "Weather_OWM_Forecast_Rain_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number rain = ScriptServiceUtil.getItemRegistry.getItem(rainItemName).state as Number
        rain_day_2 = rain_day_2 + rain
        // Snow: total
        val String snowItemName = "Weather_OWM_Forecast_Snow_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number snow = ScriptServiceUtil.getItemRegistry.getItem(snowItemName).state as Number
        snow_day_2 = snow_day_2 + snow
        // Wind speed: max
        val String windSpeedItemName = "Weather_OWM_Forecast_Wind_Speed_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number wind_speed = ScriptServiceUtil.getItemRegistry.getItem(windSpeedItemName).state as Number
        if (wind_speed_max_day_2 < wind_speed) wind_speed_max_day_2 = wind_speed
        // Temperature: min, max
        val String tempItemName = "Weather_OWM_Forecast_Temperature_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number temp = ScriptServiceUtil.getItemRegistry.getItem(tempItemName).state as Number
        if (temp_max_day_2 < temp) temp_max_day_2 = temp
        if (temp_min_day_2 > temp) temp_min_day_2 = temp
        // Humidity: min, max
        val String humidityItemName = "Weather_OWM_Forecast_Humidity_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number humidity = ScriptServiceUtil.getItemRegistry.getItem(humidityItemName).state as Number
        if (humidity_max_day_2 < humidity) humidity_max_day_2 = humidity
        if (humidity_min_day_2 > humidity) humidity_min_day_2 = humidity
        // Pressure: min, max
        val String pressureItemName = "Weather_OWM_Forecast_Pressure_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number pressure = ScriptServiceUtil.getItemRegistry.getItem(pressureItemName).state as Number
        if (pressure_max_day_2 < pressure) pressure_max_day_2 = pressure
        if (pressure_min_day_2 > pressure) pressure_min_day_2 = pressure

        logDebug(ruleTitle,
            "Weather forecast for 'today + {} day(s)' at {}h ({}h + {}h) -- {} = {}, rain: {}, snow: {}, wind speed: {}, pressure: {}, humidity: {}",
            2 + forecastDayOffset, forecast_03h_h + i - 3 - 48, forecast_03h_h, i - 3 - 48,
            tempItemName, temp, rain, snow, wind_speed, pressure, humidity)
        i = i + 3
    }
    logInfo(ruleTitle,
        "Forecast in {} days: temperature: {} - {}, rain: {}, snow: {}, wind speed: {}, pressure: {} - {}, humidity: {} - {}",
        2 + forecastDayOffset,
        temp_min_day_2, temp_max_day_2, rain_day_2, snow_day_2, wind_speed_max_day_2,
        pressure_min_day_2, pressure_max_day_2, humidity_min_day_2, humidity_max_day_2)

    var Number rain_day_3 = 0|mm
    var Number snow_day_3 = 0|mm
    var Number wind_speed_max_day_3 = 0|"km/h"
    var Number temp_min_day_3 = 100|°C // Make it a very high (!) temperature so it gets updated instantly
    var Number temp_max_day_3 = -100|°C // Make it a very low (!) temperature so it gets updated instantly
    var Number humidity_min_day_3 = 100 // Make it a 100% so it gets updated instantly
    var Number humidity_max_day_3 = 0 // Make it 0% so it gets updated instantly
    var Number pressure_min_day_3 = 2000|"hPa" // Make it a 100% so it gets updated instantly
    var Number pressure_max_day_3 = 0|"hPa" // Make it 0% so it gets updated instantly

    while ( forecast_03h_h + i - 3 <= 96 ) {
        // Rain: total
        val String rainItemName = "Weather_OWM_Forecast_Rain_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number rain = ScriptServiceUtil.getItemRegistry.getItem(rainItemName).state as Number
        rain_day_3 = rain_day_3 + rain
        // Snow: total
        val String snowItemName = "Weather_OWM_Forecast_Snow_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number snow = ScriptServiceUtil.getItemRegistry.getItem(snowItemName).state as Number
        snow_day_3 = snow_day_3 + snow
        // Wind speed: max
        val String windSpeedItemName = "Weather_OWM_Forecast_Wind_Speed_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number wind_speed = ScriptServiceUtil.getItemRegistry.getItem(windSpeedItemName).state as Number
        if (wind_speed_max_day_3 < wind_speed) wind_speed_max_day_3 = wind_speed
        // Temperature: min, max
        val String tempItemName = "Weather_OWM_Forecast_Temperature_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number temp = ScriptServiceUtil.getItemRegistry.getItem(tempItemName).state as Number
        if (temp_max_day_3 < temp) temp_max_day_3 = temp
        if (temp_min_day_3 > temp) temp_min_day_3 = temp
        // Humidity: min, max
        val String humidityItemName = "Weather_OWM_Forecast_Humidity_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number humidity = ScriptServiceUtil.getItemRegistry.getItem(humidityItemName).state as Number
        if (humidity_max_day_3 < humidity) humidity_max_day_3 = humidity
        if (humidity_min_day_3 > humidity) humidity_min_day_3 = humidity
        // Pressure: min, max
        val String pressureItemName = "Weather_OWM_Forecast_Pressure_" + (if(i<10) {"0" + i.toString } else { i.toString }) + "h"
        val Number pressure = ScriptServiceUtil.getItemRegistry.getItem(pressureItemName).state as Number
        if (pressure_max_day_3 < pressure) pressure_max_day_3 = pressure
        if (pressure_min_day_3 > pressure) pressure_min_day_3 = pressure

        logDebug(ruleTitle,
            "Weather forecast for 'today + {} day(s)' at {}h ({}h + {}h) -- {} = {}, rain: {}, snow: {}, wind speed: {}, pressure: {}, humidity: {}",
            3 + forecastDayOffset, forecast_03h_h + i - 3 - 72, forecast_03h_h, i - 3 - 72,
            tempItemName, temp, rain, snow, wind_speed, pressure, humidity)
        i = i + 3
    }
    logInfo(ruleTitle,
        "Forecast in {} days: temperature: {} - {}, rain: {}, snow: {}, wind speed: {}, pressure: {} - {}, humidity: {} - {}",
        3 + forecastDayOffset,
        temp_min_day_3, temp_max_day_3, rain_day_3, snow_day_3, wind_speed_max_day_3,
        pressure_min_day_3, pressure_max_day_3, humidity_min_day_3, humidity_max_day_3)

    if (forecastDayOffset == 0) {
        Weather_OWM_Forecast_Rain_Day0.postUpdate(rain_day_0)
        Weather_OWM_Forecast_Rain_Day1.postUpdate(rain_day_1)
        Weather_OWM_Forecast_Rain_Day2.postUpdate(rain_day_2)
        Weather_OWM_Forecast_Rain_Day3.postUpdate(rain_day_3)

        Weather_OWM_Forecast_Snow_Day0.postUpdate(snow_day_0)
        Weather_OWM_Forecast_Snow_Day1.postUpdate(snow_day_1)
        Weather_OWM_Forecast_Snow_Day2.postUpdate(snow_day_2)
        Weather_OWM_Forecast_Snow_Day3.postUpdate(snow_day_3)

        Weather_OWM_Forecast_Wind_Speed_Max_Day0.postUpdate(wind_speed_max_day_0)
        Weather_OWM_Forecast_Wind_Speed_Max_Day1.postUpdate(wind_speed_max_day_1)
        Weather_OWM_Forecast_Wind_Speed_Max_Day2.postUpdate(wind_speed_max_day_2)
        Weather_OWM_Forecast_Wind_Speed_Max_Day3.postUpdate(wind_speed_max_day_3)

        Weather_OWM_Forecast_Temperature_Min_Day0.postUpdate(temp_min_day_0)
        Weather_OWM_Forecast_Temperature_Min_Day1.postUpdate(temp_min_day_1)
        Weather_OWM_Forecast_Temperature_Min_Day2.postUpdate(temp_min_day_2)
        Weather_OWM_Forecast_Temperature_Min_Day3.postUpdate(temp_min_day_3)

        Weather_OWM_Forecast_Temperature_Max_Day0.postUpdate(temp_max_day_0)
        Weather_OWM_Forecast_Temperature_Max_Day1.postUpdate(temp_max_day_1)
        Weather_OWM_Forecast_Temperature_Max_Day2.postUpdate(temp_max_day_2)
        Weather_OWM_Forecast_Temperature_Max_Day3.postUpdate(temp_max_day_3)

        Weather_OWM_Forecast_Pressure_Min_Day0.postUpdate(pressure_min_day_0)
        Weather_OWM_Forecast_Pressure_Min_Day1.postUpdate(pressure_min_day_1)
        Weather_OWM_Forecast_Pressure_Min_Day2.postUpdate(pressure_min_day_2)
        Weather_OWM_Forecast_Pressure_Min_Day3.postUpdate(pressure_min_day_3)

        Weather_OWM_Forecast_Pressure_Max_Day0.postUpdate(pressure_max_day_0)
        Weather_OWM_Forecast_Pressure_Max_Day1.postUpdate(pressure_max_day_1)
        Weather_OWM_Forecast_Pressure_Max_Day2.postUpdate(pressure_max_day_2)
        Weather_OWM_Forecast_Pressure_Max_Day3.postUpdate(pressure_max_day_3)

        Weather_OWM_Forecast_Humidity_Min_Day0.postUpdate(humidity_min_day_0)
        Weather_OWM_Forecast_Humidity_Min_Day1.postUpdate(humidity_min_day_1)
        Weather_OWM_Forecast_Humidity_Min_Day2.postUpdate(humidity_min_day_2)
        Weather_OWM_Forecast_Humidity_Min_Day3.postUpdate(humidity_min_day_3)

        Weather_OWM_Forecast_Humidity_Max_Day0.postUpdate(humidity_max_day_0)
        Weather_OWM_Forecast_Humidity_Max_Day1.postUpdate(humidity_max_day_1)
        Weather_OWM_Forecast_Humidity_Max_Day2.postUpdate(humidity_max_day_2)
        Weather_OWM_Forecast_Humidity_Max_Day3.postUpdate(humidity_max_day_3)

    } else {

        Weather_OWM_Forecast_Rain_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Rain_Day1.postUpdate(rain_day_0)
        Weather_OWM_Forecast_Rain_Day2.postUpdate(rain_day_1)
        Weather_OWM_Forecast_Rain_Day3.postUpdate(rain_day_2)

        Weather_OWM_Forecast_Snow_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Snow_Day1.postUpdate(snow_day_0)
        Weather_OWM_Forecast_Snow_Day2.postUpdate(snow_day_1)
        Weather_OWM_Forecast_Snow_Day3.postUpdate(snow_day_2)

        Weather_OWM_Forecast_Wind_Speed_Max_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Wind_Speed_Max_Day1.postUpdate(wind_speed_max_day_0)
        Weather_OWM_Forecast_Wind_Speed_Max_Day2.postUpdate(wind_speed_max_day_1)
        Weather_OWM_Forecast_Wind_Speed_Max_Day3.postUpdate(wind_speed_max_day_2)

        Weather_OWM_Forecast_Temperature_Min_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Temperature_Min_Day1.postUpdate(temp_min_day_0)
        Weather_OWM_Forecast_Temperature_Min_Day2.postUpdate(temp_min_day_1)
        Weather_OWM_Forecast_Temperature_Min_Day3.postUpdate(temp_min_day_2)

        Weather_OWM_Forecast_Temperature_Max_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Temperature_Max_Day1.postUpdate(temp_max_day_0)
        Weather_OWM_Forecast_Temperature_Max_Day2.postUpdate(temp_max_day_1)
        Weather_OWM_Forecast_Temperature_Max_Day3.postUpdate(temp_max_day_2)

        Weather_OWM_Forecast_Pressure_Min_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Pressure_Min_Day1.postUpdate(pressure_min_day_0)
        Weather_OWM_Forecast_Pressure_Min_Day2.postUpdate(pressure_min_day_1)
        Weather_OWM_Forecast_Pressure_Min_Day3.postUpdate(pressure_min_day_2)

        Weather_OWM_Forecast_Pressure_Max_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Pressure_Max_Day1.postUpdate(pressure_max_day_0)
        Weather_OWM_Forecast_Pressure_Max_Day2.postUpdate(pressure_max_day_1)
        Weather_OWM_Forecast_Pressure_Max_Day3.postUpdate(pressure_max_day_2)

        Weather_OWM_Forecast_Humidity_Min_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Humidity_Min_Day1.postUpdate(humidity_min_day_0)
        Weather_OWM_Forecast_Humidity_Min_Day2.postUpdate(humidity_min_day_1)
        Weather_OWM_Forecast_Humidity_Min_Day3.postUpdate(humidity_min_day_2)

        Weather_OWM_Forecast_Humidity_Max_Day0.postUpdate(UNDEF)
        Weather_OWM_Forecast_Humidity_Max_Day1.postUpdate(humidity_max_day_0)
        Weather_OWM_Forecast_Humidity_Max_Day2.postUpdate(humidity_max_day_1)
        Weather_OWM_Forecast_Humidity_Max_Day3.postUpdate(humidity_max_day_2)

    }
end

```

My rule separately computes the values for a day at a time (hence the “`_0`”, “`_1`” etc. variable numbering in 4 quasi-similar code blocks). It takes care of the edge case where the first forecast value is no longer for today (then `forecastDayOffset` is set to `1`). For this reason I can only compute the forecast values for up to 3 days in advance. The 4th day is not processed as it can only use a full forecast data set when the forecast starts at the 1st possible time slot of a day (so the 4th day uses the OWM forecast data from `_75h` to `_96h`).

Have fun!

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [February 25, 2019, 8:59pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/2 "2019-02-25T20:59:10Z")

</div>

And here’s a more compact version, making use of lambda functions.

```csv
import org.eclipse.smarthome.model.script.ScriptServiceUtil
import org.eclipse.xtext.xbase.lib.Functions
import java.util.List

val int MAX_DAYS = 4

// Return the set of weather forecast readings filtered on the forecastTypeStr
val Functions$Function1<String, Iterable<GenericItem>> getForecastItems = [
    forecastTypeStr |
    gWeatherForecast.allMembers.filter[i | i.name.startsWith("Weather_OWM_Forecast_" + forecastTypeStr + "_")]
]

val Procedures$Procedure4<String, String, Boolean, List<Number>> setDailyForecastNumberItemValue = [
    forecastTypeStr, metricName, forecast_offset, valueList |
    val int MAX_DAYS = 4 // Lambda functions don't resolve global constants!
    val String itemNameBase = "Weather_OWM_Forecast_" + forecastTypeStr + ( if (metricName == "") {""} else {"_" + metricName} ) + "_Day"
    var int i = 0
    var int index = 0
    if (forecast_offset) index += 1
    while ( (i < MAX_DAYS) && (index < MAX_DAYS) ) {
        val Number value = ( if ( (forecast_offset == true) && (i == 1) ) UNDEF else valueList.get(index) )
        val String itemName = itemNameBase + i.toString
        logDebug("setDailyForecastNumberItemValue", "Value of '" + itemName + "' is: " + ScriptServiceUtil.getItemRegistry.getItem(itemName).state.toString + " - will be set to " + value.toString)
        postUpdate( ScriptServiceUtil.getItemRegistry.getItem(itemName), value )

        i += 1
        index += 1
    }
]

// Return a filtered set of forecast values for a given day
val Functions$Function3<Iterable<GenericItem>, Number, Number, Iterable<GenericItem>> filterWxItemsForDay = [
    items, forecast_03h_hour, theDay |
    val Number fromHour = 0 + 24 * theDay
    val Number toHour = 24 + 24 * theDay
    logDebug("Lambda:filterWxItemsForDay", "About to start processing the item filtering process")
    items.filter [ i |
        val Number hr = Integer::parseInt( i.name.substring(i.name.length() - 3).substring(0,2) ) as Number
        val Number hourInDay = hr + forecast_03h_hour - 3
        if ( (hourInDay >= fromHour) && (hourInDay <= toHour) ) {
            logDebug("Lambda:filterWxItemsForDay", i.name + ": hour " + hourInDay.toString + " = " + i.state.toString)
        }

        // Result:
        (hourInDay >= fromHour) && (hourInDay <= toHour)
    ]
]

val Functions$Function1<Iterable<GenericItem>, Number> maxStateValueFrom = [
    items |
    items.map[state as Number].reduce[ max, v |
        if (v > max)
            v
        else
            max
    ]
]

val Functions$Function1<Iterable<GenericItem>, Number> minStateValueFrom = [
    items |
    items.map[state as Number].reduce[ min, v |
        if (v < min)
            v
        else
            min
    ]
]

val Functions$Function1<Iterable<GenericItem>, Number> sumStateValueFrom = [
    items |
    items.map[state as Number].reduce[ sum, v |
        sum + v
    ]
]

rule "Group-based weather forecast processing"
when
    // Time cron "0/15 * * * * ?" or
    Item DBG_Test_Weather_Forecast changed or // Debugging
    Time cron "0 0 0 * * ?" or
    Item Weather_OWM_Forecast_Time_03h changed
then
	val String ruleTitle = "UpdateOWMForecastInfo_Groups"

    val DateTime dtNow = now
    val Number now_d = dtNow.getDayOfMonth
    val Number now_m = dtNow.getMonthOfYear
    val Number now_h = dtNow.getHourOfDay

    val DateTime dtForecast_03h = (new DateTime(Weather_OWM_Forecast_Time_03h.state.toString)).toDateTime
    val Number forecast_03h_d = dtForecast_03h.getDayOfMonth
    val Number forecast_03h_m = dtForecast_03h.getMonthOfYear
    val Number forecast_03h_h = dtForecast_03h.getHourOfDay

    val Boolean forecastDayOffset = ( if (forecast_03h_d > now_d) true else false )
    if (forecastDayOffset) { // No forecast for today
        logInfo(ruleTitle, "No forecast for today ({}/{} - at or after {}h", now_d, now_m, now_h)
    }

    logInfo(ruleTitle,
        "Update Wx forecast info - today is {}/{} @ {}h - 03h forecast is for {}/{} at {}h",
        now_d, now_m, now_h,
        forecast_03h_d, forecast_03h_m, forecast_03h_h)

    val Iterable<GenericItem> forecastTemperature = getForecastItems.apply("Temperature")
    val List<Iterable<GenericItem>> temperature = newArrayList( null,null,null,null )
    val List<Number> temperature_min = newArrayList( 0,0,0,0 )
    val List<Number> temperature_max = newArrayList( 0,0,0,0 )

    val Iterable<GenericItem> forecastPressure = getForecastItems.apply("Pressure")
    val List<Iterable<GenericItem>> pressure = newArrayList( null,null,null,null )
    val List<Number> pressure_min = newArrayList( 0,0,0,0 )
    val List<Number> pressure_max = newArrayList( 0,0,0,0 )

    val Iterable<GenericItem> forecastHumidity = getForecastItems.apply("Humidity")
    val List<Iterable<GenericItem>> humidity = newArrayList( null,null,null,null )
    val List<Number> humidity_min = newArrayList( 0,0,0,0 )
    val List<Number> humidity_max = newArrayList( 0,0,0,0 )

    val Iterable<GenericItem> forecastCloudiness = getForecastItems.apply("Cloudiness")
    val List<Iterable<GenericItem>> cloudiness = newArrayList( null,null,null,null )
    val List<Number> cloudiness_min = newArrayList( 0,0,0,0 )
    val List<Number> cloudiness_max = newArrayList( 0,0,0,0 )

    val Iterable<GenericItem> forecastRain = getForecastItems.apply("Rain")
    val List<Iterable<GenericItem>> rain = newArrayList( null,null,null,null )
    val List<Number> rain_sum = newArrayList( 0,0,0,0 )
    
    val Iterable<GenericItem> forecastSnow = getForecastItems.apply("Snow")
    val List<Iterable<GenericItem>> snow = newArrayList( null,null,null,null )
    val List<Number> snow_sum = newArrayList( 0,0,0,0 )

    val Iterable<GenericItem> forecastWind_Speed = getForecastItems.apply("Wind_Speed")
    val List<Iterable<GenericItem>> wind_speed = newArrayList( null,null,null,null )
    val List<Number> wind_speed_max = newArrayList( 0,0,0,0 )

    val forecast_offset = forecastDayOffset

    var int i = 0
    while (i < MAX_DAYS) {
        logInfo(ruleTitle, "INFO - iteration {} - forecastDayOffset is {}", i, forecastDayOffset)

        temperature.set(i, filterWxItemsForDay.apply(forecastTemperature, forecast_03h_h, i))
        temperature_min.set(i, minStateValueFrom.apply(temperature.get(i)))
        temperature_max.set(i, maxStateValueFrom.apply(temperature.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Temperature", i, temperature_min.get(i), temperature_max.get(i) )

        pressure.set(i, filterWxItemsForDay.apply(forecastPressure, forecast_03h_h, i))
        pressure_min.set(i, minStateValueFrom.apply(pressure.get(i)))
        pressure_max.set(i, maxStateValueFrom.apply(pressure.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Pressure", i, pressure_min.get(i), pressure_max.get(i) )

        humidity.set(i, filterWxItemsForDay.apply(forecastHumidity, forecast_03h_h, i))
        humidity_min.set(i, minStateValueFrom.apply(humidity.get(i)))
        humidity_max.set(i, maxStateValueFrom.apply(humidity.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Humidity", i, humidity_min.get(i), humidity_max.get(i) )

        cloudiness.set(i, filterWxItemsForDay.apply(forecastCloudiness, forecast_03h_h, i))
        cloudiness_min.set(i, minStateValueFrom.apply(cloudiness.get(i)))
        cloudiness_max.set(i, maxStateValueFrom.apply(cloudiness.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Cloudiness", i, cloudiness_min.get(i), cloudiness_max.get(i) )

        rain.set(i, filterWxItemsForDay.apply(forecastRain, forecast_03h_h, i))
        rain_sum.set(i, sumStateValueFrom.apply(rain.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : SUM {}",
            "Rain", i, rain_sum.get(i) )

        snow.set(i, filterWxItemsForDay.apply(forecastSnow, forecast_03h_h, i))
        snow_sum.set(i, sumStateValueFrom.apply(snow.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : SUM {}",
            "Snow", i, snow_sum.get(i) )

        wind_speed.set(i, filterWxItemsForDay.apply(forecastWind_Speed, forecast_03h_h, i))
        wind_speed_max.set(i, maxStateValueFrom.apply(wind_speed.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : MAX {}",
            "Wind_Speed", i, wind_speed_max.get(i) )

        i += 1
    }

    logInfo(ruleTitle, "INFO - Updating the item values")

    setDailyForecastNumberItemValue.apply("Temperature", "Min", forecast_offset, temperature_min)
    setDailyForecastNumberItemValue.apply("Temperature", "Max", forecast_offset, temperature_max)
    setDailyForecastNumberItemValue.apply("Pressure", "Min", forecast_offset, pressure_min)
    setDailyForecastNumberItemValue.apply("Pressure", "Max", forecast_offset, pressure_max)
    setDailyForecastNumberItemValue.apply("Humidity", "Min", forecast_offset, humidity_min)
    setDailyForecastNumberItemValue.apply("Humidity", "Max", forecast_offset, humidity_max)
    setDailyForecastNumberItemValue.apply("Cloudiness", "Min", forecast_offset, cloudiness_min)
    setDailyForecastNumberItemValue.apply("Cloudiness", "Max", forecast_offset, cloudiness_max)
    setDailyForecastNumberItemValue.apply("Rain", "", forecast_offset, rain_sum)
    setDailyForecastNumberItemValue.apply("Snow", "", forecast_offset, snow_sum)
    setDailyForecastNumberItemValue.apply("Wind_Speed", "Max", forecast_offset, wind_speed_max)

end

```

To debug the rule, I added the following `Item` in my `home.items` file:

```csv
Switch DBG_Test_Weather_Forecast "Test weather forecast rule" <sun_clouds> (gDebug)

```

The `openweathermap.items` file contains the following set of Items to hold the forecast data:

```csv
// The following Items will store the computed daily forecast values

Number:Length Weather_OWM_Forecast_Rain_Day0 "Rain Total [%.1f mm]" <rain>
Number:Length Weather_OWM_Forecast_Rain_Day1 "Rain Total [%.1f mm]" <rain>
Number:Length Weather_OWM_Forecast_Rain_Day2 "Rain Total [%.1f mm]" <rain>
Number:Length Weather_OWM_Forecast_Rain_Day3 "Rain Total [%.1f mm]" <rain>

Number:Length Weather_OWM_Forecast_Snow_Day0 "Snow Total [%.1f mm]" <snow>
Number:Length Weather_OWM_Forecast_Snow_Day1 "Snow Total [%.1f mm]" <snow>
Number:Length Weather_OWM_Forecast_Snow_Day2 "Snow Total [%.1f mm]" <snow>
Number:Length Weather_OWM_Forecast_Snow_Day3 "Snow Total [%.1f mm]" <snow>

Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day0 "Temperature Low [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day1 "Temperature Low [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day2 "Temperature Low [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Min_Day3 "Temperature Low [%.1f °C]" <temperature>

Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day0 "Temperature High [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day1 "Temperature High [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day2 "Temperature High [%.1f °C]" <temperature>
Number:Temperature Weather_OWM_Forecast_Temperature_Max_Day3 "Temperature High [%.1f °C]" <temperature>

Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day0 "Max Wind Speed [%.0f km/h]" <wind>
Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day1 "Max Wind Speed [%.0f km/h]" <wind>
Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day2 "Max Wind Speed [%.0f km/h]" <wind>
Number:Speed Weather_OWM_Forecast_Wind_Speed_Max_Day3 "Max Wind Speed [%.0f km/h]" <wind>

Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day0 "Pressure Low [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day1 "Pressure Low [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day2 "Pressure Low [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Min_Day3 "Pressure Low [%.1f hPa]" <pressure>

Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day0 "Pressure High [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day1 "Pressure High [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day2 "Pressure High [%.1f hPa]" <pressure>
Number:Pressure Weather_OWM_Forecast_Pressure_Max_Day3 "Pressure High [%.1f hPa]" <pressure>

Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day0 "Humidity Low [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day1 "Humidity Low [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day2 "Humidity Low [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Min_Day3 "Humidity Low [%d %unit%]" <humidity>

Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day0 "Humidity High [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day1 "Humidity High [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day2 "Humidity High [%d %unit%]" <humidity>
Number:Dimensionless Weather_OWM_Forecast_Humidity_Max_Day3 "Humidity High [%d %unit%]" <humidity>

Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Min_Day0 "Cloudiness Low [%d %unit%]" <sun_clouds>
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Min_Day1 "Cloudiness Low [%d %unit%]" <sun_clouds>
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Min_Day2 "Cloudiness Low [%d %unit%]" <sun_clouds>
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Min_Day3 "Cloudiness Low [%d %unit%]" <sun_clouds>

Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Max_Day0 "Cloudiness High [%d %unit%]" <sun_clouds>
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Max_Day1 "Cloudiness High [%d %unit%]" <sun_clouds>
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Max_Day2 "Cloudiness High [%d %unit%]" <sun_clouds>
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_Max_Day3 "Cloudiness High [%d %unit%]" <sun_clouds>

```

The rule now covers 124 lines of code (excluding the lambda functions) instead of 335 in my previous version.

The weather forecast is compiled by reading the OWM channel data using a strictly defined Item naming convention: all **forecast** data retrieved from the OWM binding has as format `Weather_OWM_Forecast_XXX_YYh` where:

- XXX is one of `Temperature`, `Humidity`, `Rain`, `Snow`, `Wind_Speed`, `Pressure` and `Cloudiness`; and
- YY represents the 3-hour forecast slot info (in range `03` up to `96` in steps of 3 hours (for the default free binding). They are all members of the `gWeatherForecast` group.  
For example:

```csv
Group gWeatherForecast

Number:Temperature Weather_OWM_Forecast_Temperature_03h "Temperature [%.1f %unit%]" <temperature> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#temperature"}
Number:Dimensionless Weather_OWM_Forecast_Humidity_03h "Humidity [%d %unit%]" <humidity> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#humidity"}
Number:Speed Weather_OWM_Forecast_Wind_Speed_03h "Wind speed [%.0f %unit%]" <wind> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#wind-speed"}
Number:Pressure Weather_OWM_Forecast_Pressure_03h "Pressure [%.1f hPa]" <pressure> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#pressure"}
Number:Length Weather_OWM_Forecast_Rain_03h "Rain [%.1f mm]" <rain> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#rain"}
Number:Length Weather_OWM_Forecast_Snow_03h "Snow [%.1f mm]" <snow> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#snow"}
Number:Dimensionless Weather_OWM_Forecast_Cloudiness_03h "Cloudiness [%d %unit%]" <sun_clouds> (gWeatherForecast) {channel="openweathermap:weather-and-forecast:OWM_ID:local:forecastHours03#cloudiness"}

```

(Make sure to replace `OWM_ID` with your own OWM account identifier.)

What I see, is that the rule takes quite some time to run, and that it appears that there are double items in the registry when running `filterWxItemsForDay` (as I can infer from the debug logs).

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [February 25, 2019, 9:40pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/3 "2019-02-25T21:40:41Z")

</div>

You can clean up the lambda definitions a little.

```
val getForecastItems = [ String forecastTypeStr |

val setDailyForcastNumberItemValue = [ String forecastTypeStr, String metricName, Boolean forecast_offset, List<Number> valueList |

```

It is smart enough to determine whether you are creating a Functions or a Procedure based on what the last line returns (void for Procedures). This syntax used to be supported, then in OH2 the parser started to complain. But the warnings stopped appearing in the logs around 2.3.

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [February 26, 2019, 10:52am UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/4 "2019-02-26T10:52:16Z")

</div>

Okay, good to know.

However I realized that:  
`items.map[state as Number].reduce[sum, v | sum + v]`  
does not honor UoM so the `sumStateValueFrom` lambda must be rewritten.

Here’s one version that works:

```csv
val sumStateValueFrom = [
    Iterable<GenericItem> items |

    var Number sum = 0.0
    var int i = 0
    while (i < items.length) {
        val Number v = (items.get(i).state as QuantityType<Number>).doubleValue
        logDebug("sumStateValueFrom", " " + sum.toString + " + " + v.toString + " = " + ((sum+v) as Number).toString )
        sum = sum + v
        i++
    }
    sum
]

```

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [February 26, 2019, 3:20pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/5 "2019-02-26T15:20:17Z")

</div>

> [@shutterfreak](#):
>
> Here’s one version that works:

You should be able to do that in the map.

```
map[(state as QuantityType<Number>).doubleValue].reduce[sum, v | sum + v]

```

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [March 1, 2019, 1:15am UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/6 "2019-03-01T01:15:06Z")

</div>

> [@rlkoshak](#):
>
> You should be able to do that in the map.
> 
> ```auto
> map[(state as QuantityType<Number>).doubleValue].reduce[sum, v | sum + v]
> 
> ```

Thanks! That did the job.

I still find it counterintuitive to have to cast `state as QuantityType<Number>` in such cases.

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [March 1, 2019, 4:51pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/7 "2019-03-01T16:51:29Z")

</div>

There’s still something weird at play.

For some Item types it appears that the `filterWxItemsForDay` lambda processes them **twice** in a row:

```csv
2019-03-01 17:49:36.644 [INFO] [el.script.Lambda:filterWxItemsForDay] - About to start processing the item filtering process
2019-03-01 17:49:36.811 [INFO] [el.script.Lambda:filterWxItemsForDay] - In valid day range: Wx_OWM_Forecast_Cloudiness_03h: forecast hour = 3, maps to day 0 @ 19 h - value: 32 %
2019-03-01 17:49:36.944 [INFO] [el.script.Lambda:filterWxItemsForDay] - In valid day range: Wx_OWM_Forecast_Cloudiness_06h: forecast hour = 6, maps to day 0 @ 22 h - value: 88 %
2019-03-01 17:49:37.937 [INFO] [el.script.Lambda:filterWxItemsForDay] - In valid day range: Wx_OWM_Forecast_Cloudiness_03h: forecast hour = 3, maps to day 0 @ 19 h - value: 32 %
2019-03-01 17:49:38.042 [INFO] [el.script.Lambda:filterWxItemsForDay] - In valid day range: Wx_OWM_Forecast_Cloudiness_06h: forecast hour = 6, maps to day 0 @ 22 h - value: 88 %
2019-03-01 17:49:38.639 [INFO] [.script.UpdateOWMForecastInfo_Groups] - RESULT: Cloudiness FORECAST FOR DAY 0 : 32 % -- 88 %
2019-03-01 17:49:38.644 [INFO] [el.script.Lambda:filterWxItemsForDay] - About to start processing the item filtering process
2019-03-01 17:49:38.895 [INFO] [el.script.Lambda:filterWxItemsForDay] - In valid day range: Wx_OWM_Forecast_Rain_03h: forecast hour = 3, maps to day 0 @ 19 h - value: 0.0 mm
2019-03-01 17:49:38.973 [INFO] [el.script.Lambda:filterWxItemsForDay] - In valid day range: Wx_OWM_Forecast_Rain_06h: forecast hour = 6, maps to day 0 @ 22 h - value: 0.0 mm
2019-03-01 17:49:39.484 [INFO] [.script.UpdateOWMForecastInfo_Groups] - RESULT: Rain FORECAST FOR DAY 0 : SUM 0.0

```

Here’s the code of the lambda:

```csv
// Return a filtered set of forecast values for a given day
val filterWxItemsForDay = [
    Iterable<GenericItem> items, // The weather forecast items for a given forecast type (all time values)
    Number forecast_03h_hour, // The hour-in-day offset for the first forecast values
    Number theDay | // The forecast day to filter the forecast values from for subsequent per-day processing

    val Number fromHour = 0 + 24 * theDay
    val Number toHour = 24 + 24 * theDay
    logInfo("Lambda:filterWxItemsForDay", "About to start processing the item filtering process")
    items.filter [ i |
        val String ext = i.name.substring(i.name.length() - 4).substring(0,3)
        var Number hr = Integer::parseInt( ext.substring(1,3) )
        if (! ext.startsWith("_") ) {
            hr += 100 * Integer::parseInt( ext.substring(0,1) )
        }
        logDebug("Lambda:filterWxItemsForDay", i.name + ": ext '" + ext + "' - hr = " + hr.toString + " - day " + theDay.toString )

        val Number hourInDay = hr + forecast_03h_hour - 3
        if ( (hourInDay >= fromHour) && (hourInDay <= toHour) ) {
            logInfo("Lambda:filterWxItemsForDay", "In valid day range: " + i.name + ": forecast hour = " + hr.toString + ", maps to day " + theDay.toString + " @ " + (hourInDay - 24 * theDay).toString + " h - value: " + i.state.toString)
        }

        // Filter per-item result (Boolean):
        (hourInDay >= fromHour) && (hourInDay <= toHour)
    ] // Lambda return type is: Iterable<GenericItem>
]

```

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [March 1, 2019, 5:17pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/8 "2019-03-01T17:17:33Z")

</div>

> [@shutterfreak](#):
>
> Here’s the code of the lambda:

If this is the case, the code of the lambda isn’t going to help. You need to post all the Rules that call the lambda.

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [March 1, 2019, 6:58pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/9 "2019-03-01T18:58:02Z")

</div>

> [@rlkoshak](#):
>
> > Here’s the code of the lambda:
> 
> If this is the case, the code of the lambda isn’t going to help. You need to post all the Rules that call the lambda.

There is only one rule that calls it: `rule "Group-based weather forecast processing" `. Here’s the full code of my `openweathermap.rules` file:

```csv
import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.List

val int MAX_DAYS = 5

/* Return the set of all weather forecast readings for a specific reading (stored in forecastTypeStr).
 * This is possible since all weather forecast items obey the same naming convention:
 * Wx_OWM_Forecast_<forecastType>_<hourInterval>h
 * where hourInterval starts at 03 and goes up to 120.
 */
val getForecastItems = [
    String forecastTypeStr | // "Temperature", "Humidity", "Pressure", "Wind_Speed", "Rain", "Snow", "Cloudiness"

    gWeatherForecast.allMembers.filter[ i |
        i.name.startsWith("Wx_OWM_Forecast_" + forecastTypeStr + "_")
    ] as Iterable<GenericItem>
]

/* Update the state of the daily Wx_OWM_Forecast_<TYPE>[_<METRIC>]_Day<N> by setting the proper values from the provided list.
 * When the first forecast value (_03h) is no longer for the current day, we must skip the forecast for the current day (N=0).
 */
val setDailyForecastNumberItemValue = [
    String forecastTypeStr, // "Temperature", "Humidity", "Pressure", "Wind_Speed", "Rain", "Snow", "Cloudiness"
    String metricName, // "Min", "Max", "Sum"
    Boolean forecast_offset, // True if no forecast info for today (offset 1 day)
    List<Number> valueList |

    val int MAX_DAYS = 5 // Lambda functions don't resolve global constants!
    val String itemNameBase = "Wx_OWM_Forecast_" + forecastTypeStr + ( if (metricName == "") {""} else {"_" + metricName} ) + "_Day"
    var int i = 0
    var int index = 0
    if (forecast_offset) index += 1
    while ( (i < MAX_DAYS) && (index < MAX_DAYS) ) {
        val String itemName = itemNameBase + i.toString
        val Boolean postUndef = ( (forecast_offset == true) && (i == 1) )
        val Number value = ( if ( postUndef ) { -1 } else { valueList.get(index) } )
        val GenericItem forecastItem = ScriptServiceUtil.getItemRegistry.getItem(itemName) as GenericItem
        logDebug("setDailyForecastNumberItemValue", "Processing '" + forecastItem + "'")
        logDebug("setDailyForecastNumberItemValue", "Value of '" + itemName + "' is: "
            + forecastItem.state.toString + " - will be set to " + value.toString)
        if (postUndef) {
            postUpdate( forecastItem, UNDEF )
        } else {
            postUpdate( forecastItem, value )
        }

        i += 1
        index += 1
    }
]

/* Return the subset of weather forecast items that apply for the day specified (from midnight to midnight),
 * where day 0 is the current day. With a free OWM account, this results in at most 8 items per day (24h / 3h forecasts = 8 daily forecasts)
 */
val filterWxItemsForDay = [
    Iterable<GenericItem> items, // The weather forecast items for a given forecast type (all time values)
    Number forecast_03h_hour, // The hour-in-day offset for the first forecast values
    Number theDay | // The forecast day to filter the forecast values from for subsequent per-day processing

    val Number fromHour = 0 + 24 * theDay
    val Number toHour = 24 + 24 * theDay
    logInfo("Lambda:filterWxItemsForDay", "About to start processing the item filtering process")
    items.filter [ i |
        val String ext = i.name.substring(i.name.length() - 4).substring(0,3)
        var Number hr = Integer::parseInt( ext.substring(1,3) )
        if (! ext.startsWith("_") ) {
            hr += 100 * Integer::parseInt( ext.substring(0,1) )
        }
        logDebug("Lambda:filterWxItemsForDay", i.name + ": ext '" + ext + "' - hr = " + hr.toString + " - day " + theDay.toString )

        val Number hourInDay = hr + forecast_03h_hour - 3
        if ( (hourInDay >= fromHour) && (hourInDay <= toHour) ) {
            logInfo("Lambda:filterWxItemsForDay", "In valid day range: " + i.name + ": forecast hour = " + hr.toString + ", maps to day " + theDay.toString + " @ " + (hourInDay - 24 * theDay).toString + " h - value: " + i.state.toString)
        }

        // Filter per-item result (Boolean):
        (hourInDay >= fromHour) && (hourInDay <= toHour)
    ] // Lambda return type is: Iterable<GenericItem>
]

/* Return the maximum from a list of Number values (preserving the Units of Measurement)
 */
val maxStateValueFrom = [
    Iterable<GenericItem> items |

    items.map[state as Number].reduce[ max, v |
        if (v > max)
            v
        else
            max
    ] // Lambda return type is: Number
]

/* Return the minimum from a list of Number values (preserving the Units of Measurement)
 */
val minStateValueFrom = [
    Iterable<GenericItem> items |

    items.map[state as Number].reduce[ min, v |
        if (v < min)
            v
        else
            min
    ] // Lambda return type is: Number
]

/* Return the sum from a list of Number values (preserving the Units of Measurement)
 */
val sumStateValueFrom = [
    Iterable<GenericItem> items |

    items.map[(state as QuantityType<Number>).doubleValue].reduce[sum, v | sum + v]
]

/* Set Wx_OWM_Current_NightState at startup
 */
rule "OpenHAB system started - astro"
when
    System started
then
    createTimer(now.plusSeconds(180)) [ |
        if (now.isAfter((Astro_Sunset_End.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) ||
            now.isBefore((Astro_Sunrise_Start.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
        ) {
            postUpdate(Wx_OWM_Current_NightState, ON)
        } else {
            postUpdate(Wx_OWM_Current_NightState, OFF)
        }
    ]
end

/* Update Wx_OWM_Current_NightState based on the elevation of the sun (night: elevation < 0).
 * Also set the weather condition mapping from day to night based on Wx_OWM_Current_NightState
 */
rule "Update Night State"
when
    Item Astro_Sun_Elevation changed
then
    if (Astro_Sun_Elevation.state > 0|°){
        if (Wx_OWM_Current_NightState.state != OFF) {
            postUpdate(Wx_OWM_Current_NightState, OFF)
            // if Wx_OWM_Current_NightState was ON we need to update Wx_OWM_Current_Condition_Formatted too
            Wx_OWM_Current_Condition_Formatted.postUpdate(transform("MAP", "openweathermap_day.map", Wx_OWM_Current_Condition_Id.state.toString()))
        }
    } else {
        if (Wx_OWM_Current_NightState.state != ON) {
            postUpdate(Wx_OWM_Current_NightState, ON)
            Wx_OWM_Current_Condition_Formatted.postUpdate(transform("MAP", "openweathermap_night.map", Wx_OWM_Current_Condition_Id.state.toString()))
        }
    }
end

/* Provide the compass wind direction in a proxy Item (Wx_OWM_Current_Wind_Direction_Simplified) for displaying on the sitemap.
 * This is needed as the SCALE transform only accepts numeric values, whereas the OWM binding sometimes reports non-numeric values
 * in cases where the wind direction is undefined or hard to define.
 */
rule "Update simplified wind direction"
when
	Item Wx_OWM_Current_Wind_Direction changed
then
    val String ruleTitle = "UpdateOWMCurrentWindDirection"
// Update Wx_OWM_Current_Wind_Direction_Simplified if Wx_OWM_Wind_Speed is numeric
    var String status = ""
    var boolean error = true
    if (Wx_OWM_Current_Wind_Direction.state == NULL) {
        status = "(not set)"
    } else if (Wx_OWM_Current_Wind_Direction.state == UNDEF) {
        status = "(undefined)"
    } else if (Wx_OWM_Current_Wind_Direction.state instanceof Number) {
        status = transform("SCALE", "wind.scale", Wx_OWM_Current_Wind_Direction.state.toString())
        error = false
    } else { // Unexpected state type
        status = "(invalid: " + Wx_OWM_Current_Wind_Direction.state.toString() + ")"
    }
    if (error) {
        logWarn(ruleTitle, "{} has state '{}', expecting Number", Wx_OWM_Current_Wind_Direction.name, Wx_OWM_Current_Wind_Direction.state.toString())
    } else {
        logDebug(ruleTitle, "{} has Number state '{}'", Wx_OWM_Current_Wind_Direction.name, Wx_OWM_Current_Wind_Direction.state.toString())
    }
    postUpdate(Wx_OWM_Current_Wind_Direction_Simplified, status)
end

/* Compute daiy forecast values from the hourly forecast values provided by the OpenWeatherMap binding.
 *
 */
rule "Group-based weather forecast processing"
when
    Item DBG_Test_Weather_Forecast changed or // Debugging
    Time cron "0 0 0 * * ?" or
    Item Wx_OWM_Forecast_Time_03h changed
then
	val String ruleTitle = "UpdateOWMForecastInfo_Groups"

    // Compute the rule execution time, making sure we use the same 'now' value for computing month, day and hour
    val DateTime dtNow = now
    val Number now_d = dtNow.getDayOfMonth
    val Number now_m = dtNow.getMonthOfYear
    val Number now_h = dtNow.getHourOfDay

    // Compute the month, day and hour of the first forecast time (3 hour offset)
    val DateTime dtForecast_03h = (new DateTime(Wx_OWM_Forecast_Time_03h.state.toString)).toDateTime
    val Number forecast_03h_d = dtForecast_03h.getDayOfMonth
    val Number forecast_03h_m = dtForecast_03h.getMonthOfYear
    val Number forecast_03h_h = dtForecast_03h.getHourOfDay

    // If the first forecast value doesn't apply for the current day (e.g., at 22:00h the 1st forecast is for the next day at 22:00 +3h = 01:00),
    // then set 'forecastDayOffset' to 'true' for further processing. In this edge case, we won't display today's forecast as it is nonexistent.
    val Boolean forecastDayOffset = ( if (forecast_03h_d > now_d) true else false )

    // Store the forecastDayOffset value in the proxy item Wx_OWM_Forecast_Day_Offset so it is available in sitemaps and UIs:
    if (forecastDayOffset) { // No forecast for today
        logInfo(ruleTitle, "No forecast for today ({}/{} - at or after {}h", now_d, now_m, now_h)
        if (Wx_OWM_Forecast_Day_Offset.state !== ON) {
            postUpdate(Wx_OWM_Forecast_Day_Offset, ON)
        }
    } else {
        if (Wx_OWM_Forecast_Day_Offset.state !== OFF) {
            postUpdate(Wx_OWM_Forecast_Day_Offset, OFF)
        }
    }

    logInfo(ruleTitle,
        "Update Wx forecast info - today is {}/{} @ {}h - 03h forecast is for {}/{} at {}h",
        now_d, now_m, now_h,
        forecast_03h_d, forecast_03h_m, forecast_03h_h)

    // Initialize the lists that will contain the daily forecast items we want to compute.
    // With a free OWM account, you can get 5 day forecast values per 3 hours, which covers 120 hours.
    // Only if the first forecast timestamp falls within the first 3 hours of a day (the day after the current day),
    // we can compute the daily forecast for 5 days in a row. In other cases, we will compute the daily forecast for today + 4 days.
    // Today's forecast is computed by considering the current values plus the set of forecast values that fit in the current day.

    // The daily forecast values will be stored in array lists, hence we initialize them:
    //
    // - val Iterable<GenericItem> forecast[Quantity] contains the list of [Quantity] forecast Items, filtered from
    // the 'gWeatherForecast' Item group by means of the lambda 'getForecastItems'
    // - val List<Iterable<GenericItem>> [quantity] will contain the forecast[Quantity] list, but ordered in sets of forecast values
    // for a given forecast day (0 = today, 1 = tomorrow, etc), with the lambda 'filterWxItemsForDay'
    // - val List<Number> [quantity]_min will contain the minimum value of [quantity] for each forecast day (Temperature, Pressure...)
    // - val List<Number> [quantity]_max will contain the maximum value of [quantity] for each forecast day (Temperature, Wind Speed...)
    // - val List<Number> [quantity]_sum will contain the daily sum of [quantity] for each forecast day (Rain, Snow)
    //
    // The min, max and sum are also comuted with lambda functions.

    // Temperature: compute daily min, max
    val Iterable<GenericItem> forecastTemperature = getForecastItems.apply("Temperature")
    val List<Iterable<GenericItem>> temperature = newArrayList( null,null,null,null,null )
    val List<Number> temperature_min = newArrayList( 0,0,0,0,0 )
    val List<Number> temperature_max = newArrayList( 0,0,0,0,0 )

    // Pressure: compute daily min, max
    val Iterable<GenericItem> forecastPressure = getForecastItems.apply("Pressure")
    val List<Iterable<GenericItem>> pressure = newArrayList( null,null,null,null,null )
    val List<Number> pressure_min = newArrayList( 0,0,0,0,0 )
    val List<Number> pressure_max = newArrayList( 0,0,0,0,0 )

    // Humidity: compute daily min, max
    val Iterable<GenericItem> forecastHumidity = getForecastItems.apply("Humidity")
    val List<Iterable<GenericItem>> humidity = newArrayList( null,null,null,null,null )
    val List<Number> humidity_min = newArrayList( 0,0,0,0,0 )
    val List<Number> humidity_max = newArrayList( 0,0,0,0,0 )

    // Cloudiness: compute daily min, max
    val Iterable<GenericItem> forecastCloudiness = getForecastItems.apply("Cloudiness")
    val List<Iterable<GenericItem>> cloudiness = newArrayList( null,null,null,null,null )
    val List<Number> cloudiness_min = newArrayList( 0,0,0,0,0 )
    val List<Number> cloudiness_max = newArrayList( 0,0,0,0,0 )

    // Rain: compute daily sum
    val Iterable<GenericItem> forecastRain = getForecastItems.apply("Rain")
    val List<Iterable<GenericItem>> rain = newArrayList( null,null,null,null,null )
    val List<Number> rain_sum = newArrayList( 0,0,0,0,0 )
    
    // Snow: compute daily sum
    val Iterable<GenericItem> forecastSnow = getForecastItems.apply("Snow")
    val List<Iterable<GenericItem>> snow = newArrayList( null,null,null,null,null )
    val List<Number> snow_sum = newArrayList( 0,0,0,0,0 )

    // Wind Speed: compute daily max
    val Iterable<GenericItem> forecastWind_Speed = getForecastItems.apply("Wind_Speed")
    val List<Iterable<GenericItem>> wind_speed = newArrayList( null,null,null,null,null )
    val List<Number> wind_speed_max = newArrayList( 0,0,0,0,0 )

    val forecast_offset = forecastDayOffset
    logInfo(ruleTitle, "INFO - forecastDayOffset is {}", forecastDayOffset)

    // Process the forecast values, a calendar day (from midnight to midnight) at a time
    var int i = 0
    while (i < MAX_DAYS) {
        logInfo(ruleTitle, "INFO - iteration {}", i)

        // First create the daily set of forecast values for day i
        temperature.set(i, filterWxItemsForDay.apply(forecastTemperature, forecast_03h_h, i))
        // Calculate the min / max / sum for day i:
        temperature_min.set(i, minStateValueFrom.apply(temperature.get(i)))
        temperature_max.set(i, maxStateValueFrom.apply(temperature.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Temperature", i, temperature_min.get(i), temperature_max.get(i) )

        pressure.set(i, filterWxItemsForDay.apply(forecastPressure, forecast_03h_h, i))
        pressure_min.set(i, minStateValueFrom.apply(pressure.get(i)))
        pressure_max.set(i, maxStateValueFrom.apply(pressure.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Pressure", i, pressure_min.get(i), pressure_max.get(i) )

        humidity.set(i, filterWxItemsForDay.apply(forecastHumidity, forecast_03h_h, i))
        humidity_min.set(i, minStateValueFrom.apply(humidity.get(i)))
        humidity_max.set(i, maxStateValueFrom.apply(humidity.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Humidity", i, humidity_min.get(i), humidity_max.get(i) )

        cloudiness.set(i, filterWxItemsForDay.apply(forecastCloudiness, forecast_03h_h, i))
        cloudiness_min.set(i, minStateValueFrom.apply(cloudiness.get(i)))
        cloudiness_max.set(i, maxStateValueFrom.apply(cloudiness.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : {} -- {}",
            "Cloudiness", i, cloudiness_min.get(i), cloudiness_max.get(i) )

        rain.set(i, filterWxItemsForDay.apply(forecastRain, forecast_03h_h, i))
        rain_sum.set(i, sumStateValueFrom.apply(rain.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : SUM {}",
            "Rain", i, rain_sum.get(i) )

        snow.set(i, filterWxItemsForDay.apply(forecastSnow, forecast_03h_h, i))
        snow_sum.set(i, sumStateValueFrom.apply(snow.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : SUM {}",
            "Snow", i, snow_sum.get(i) )

        wind_speed.set(i, filterWxItemsForDay.apply(forecastWind_Speed, forecast_03h_h, i))
        wind_speed_max.set(i, maxStateValueFrom.apply(wind_speed.get(i)))
        logInfo(ruleTitle,"RESULT: {} FORECAST FOR DAY {} : MAX {}",
            "Wind_Speed", i, wind_speed_max.get(i) )

        // Advance one day
        i += 1
    }

    logInfo(ruleTitle, "INFO - Updating the item values")

    // Now the daily forecast values can be propagated to the proxy Items, using the lambda 'setDailyForecastNumberItemValue'.
    // This lambda will take care of the forecast offset in case it is needed
    setDailyForecastNumberItemValue.apply("Temperature", "Min", forecast_offset, temperature_min)
    setDailyForecastNumberItemValue.apply("Temperature", "Max", forecast_offset, temperature_max)
    setDailyForecastNumberItemValue.apply("Pressure", "Min", forecast_offset, pressure_min)
    setDailyForecastNumberItemValue.apply("Pressure", "Max", forecast_offset, pressure_max)
    setDailyForecastNumberItemValue.apply("Humidity", "Min", forecast_offset, humidity_min)
    setDailyForecastNumberItemValue.apply("Humidity", "Max", forecast_offset, humidity_max)
    setDailyForecastNumberItemValue.apply("Cloudiness", "Min", forecast_offset, cloudiness_min)
    setDailyForecastNumberItemValue.apply("Cloudiness", "Max", forecast_offset, cloudiness_max)
    setDailyForecastNumberItemValue.apply("Rain", "", forecast_offset, rain_sum)
    setDailyForecastNumberItemValue.apply("Snow", "", forecast_offset, snow_sum)
    setDailyForecastNumberItemValue.apply("Wind_Speed", "Max", forecast_offset, wind_speed_max)

end

```

Note: I am aware of the existence of a bug that occurs once daily. It will get fixed when I have some time; I’m now collecting debug logs over the course of 24 hours to identify the logic error. But that has nothing to do with the duplicate processing seen in `filterWxItemsForDay`.

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [March 1, 2019, 7:26pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/10 "2019-03-01T19:26:43Z")

</div>

Honestly, this code is very long and very complex and I have no idea how it’s supposed to work.

Based on the logs, it looks like you have Items in the items Iterable that you pass into the lambda somehow. Beyond that I don’t have the hour or two it would take to just read the full bit of code and understand how it works.

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [March 2, 2019, 12:10pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/11 "2019-03-02T12:10:26Z")

</div>

> [@rlkoshak](#):
>
> Honestly, this code is very long and very complex and I have no idea how it’s supposed to work.

I updated the rules code and provided comments where required to better understand how the rule works. Best is to start reading only `rule "Group-based weather forecast processing"` and then go to the lambda functions when you encounter them in the rule execution.

I hope I managed to clarify my rule-based daily weather forecast processing.

---

<div class="post-metadata">

**Author:** ![LukaNoah](https://community.openhab.org/letter_avatar_proxy/v4/letter/l/bc79bd/32.png) [@LukaNoah](https://community.openhab.org/u/LukaNoah)\
**Post date:** [March 2, 2019, 6:12pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/12 "2019-03-02T18:12:58Z")

</div>

hello,  
can you please provide all current files needed? I get a lot of errors because item names seem to be wrong. Thank you very much.

PS. Would not that be something for a direct implementation in the binding itself? You would save a lot of items …

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [March 2, 2019, 10:27pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/13 "2019-03-02T22:27:35Z")

</div>

> [@LukaNoah](#):
>
> hello,  
> can you please provide all current files needed? I get a lot of errors because item names seem to be wrong. Thank you very much.

Here you are:

1. [openweathermap.items.txt](https://community-openhab-org.s3.dualstack.eu-central-1.amazonaws.com/original/3X/c/a/ca481a4b7d009a91e3883024cb14618306aefb95.txt) (118.5 KB) → please save as **$OPENHAB\_CONF/items/openweathermap.items** after downloading
2. [openweathermap.rules.txt](https://community-openhab-org.s3.dualstack.eu-central-1.amazonaws.com/original/3X/5/a/5a40fa5819efd09a04563a596bf9a424d47b8be8.txt) (21.1 KB) → please save as **$OPENHAB\_CONF/rules/openweathermap.rules** after downloading

As a **bonus** you’ll see that I now also generate **range items** that will display the minimum and maximum values in one go (instead of showing them separately), as with:

```csv
String Wx_OWM_Forecast_Temperature_Range_Day0 "Temperature [%s]" <temperature>

```

I use a rule to compute the state (value) of that Item. The only drawback is that you lose UoM conversion capabilities (e.g. wind speed in my locale is expressed in m/s while most of us are more familiarised with wind speeds in km/h (basically multiplying the values by 3.6 and changing the unit). So my sitemap will properly render min and max wind speeds in km/h but my rule hack will express the values in m/s (with 2-digit precision - something else I can’t easily change in rules).

FWIW I don’t think OpenHAB today allows multiple `Number` UoM values displayed in one `Item` while using UoM conversion. It would be great if OpenHAB would allow the following Item definitions:

```csv
Number:Speed	Wx_OWM_Forecast_Wind_Speed_Range_Day0 "Wind Speed [$1%.0f km/h - $2%.0f km/h]" <wind>	(gWeatherForecastDaily)

```

effectively referring to 2 `Number:Speed` values rendered as `"[%.0f km/h - %.0f km/h]"`. But that’s a different story. Back to my OWM daily forecast compilation. Now comes the visualisation part.

In your sitemap:

```csv
sitemap home label="Our Home" icon="home" {

	Frame label="Weather" icon="temperature" {
	
		Text item=Wx_OWM_Current_Observation_Time label="Current weather [%1$tA, %1$tb %1$td, %1$tY]" icon="sun_clouds" {
			// Text item=Today label="[%1$tA, %1$tb %1$td, %1$tY]" icon="calendar"
			Frame label="Calendar" icon="calendar" {
				Text item=Wx_OWM_Current_Observation_Time label="Observation for [%1$tA, %1$tb %1$td]" icon="calendar"
				Text item=Wx_OWM_Current_Observation_Time label="Observation at [%1tH:%1$tm]" icon="time"
				Default item=Wx_OWM_Current_Station_Name
				Default item=Wx_OWM_Current_Station_Id
			}

			Frame label="Current weather" icon="sun_clouds" {

				Default item=Wx_OWM_Current_Temperature label="Temperature [%.1f %unit%]" valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Current_Cloudiness
				Default item=Wx_OWM_Current_Humidity icon="humidity" label="Humidity [%d %unit%]"
				Default item=Wx_OWM_Current_Pressure
				Default item=Wx_OWM_Current_Wind_Speed icon="wind" label="Windspeed [%.0f km/h]"
				Default item=Wx_OWM_Current_Wind_Direction
				Default item=Wx_OWM_Current_Wind_Direction_Simplified
				Default item=Wx_OWM_Current_Rain label="Rain"
				Default item=Wx_OWM_Current_Snow label="Snow"
				Default item=Wx_OWM_Current_Condition icon="sun_clouds"	label="Condition [%s]"
				// Default item=Wx_OWM_Current_Condition_Formatted
				Default item=Wx_OWM_Current_ConditionIcon label="Icon [%s]"
				Default item=Wx_OWM_Current_ConditionId icon="settings" label="Condition ID [%s]"
				// Default item=Wx_OWM_Current_Condition_Img_URL label="Current condition (new image)"
			}
		}

		// If you persist the group "gWeatherCurrent" and use influxdb persistency, then you can generate the following weekly graphs:
		Text label="Graphs [OpenHAB]" icon="chart" {
			Text label="Temperature [°C]" icon="temperature"
			Chart item=Wx_OWM_Current_Temperature service="influxdb"	period=W	refresh=500000
			//
			Text label="Wind speed [m/s]" icon="wind"
			Chart item=Wx_OWM_Current_Wind_Speed service="influxdb"	period=W	refresh=500000
			//
			Text label="Humidity [%]" icon="humidity"
			Chart item=Wx_OWM_Current_Humidity service="influxdb"	period=W	refresh=500000
			//
			Text label="Coud cover [%]" icon="sun_clouds"
			Chart item=Wx_OWM_Current_Cloudiness service="influxdb"	period=W	refresh=500000
			//
			Text label="Pressure [mBar]" icon="pressure"
			Chart item=Wx_OWM_Current_Pressure service="influxdb"	period=W	refresh=500000
			//
			Text label="Rain [mm]" icon="rain"
			Chart item=Wx_OWM_Current_Rain service="influxdb"	period=W	refresh=500000
			//
			Text label="Snow [mm]" icon="snow"
			Chart item=Wx_OWM_Current_Snow service="influxdb"	period=W	refresh=500000
			//
		}

		Text item=Wx_OWM_Forecast_Time_96h label="Forecast (4 days) [until %1$tA]" icon="sun_clouds" {
			Frame item=Wx_OWM_Current_Observation_Time label="Today [%1$tA, %1$tb %1$td]" icon="calendar" visibility=[Wx_OWM_Forecast_Day_Offset==ON] {
				Text label="Warning: OWM Forecast Day Offset is TRUE, not enough forecast data for today" icon="error" // visibility=[Wx_OWM_Forecast_Day_Offset==ON]
			}
			Frame item=Wx_OWM_Current_Observation_Time label="Today [%1$tA, %1$tb %1$td]" icon="calendar" visibility=[Wx_OWM_Forecast_Day_Offset==OFF] {
				// Text label="OWM Forecast Day Offset is FALSE, enough forecast data for today" icon="settings" // visibility=[Wx_OWM_Forecast_Day_Offset==OFF]
				Default item=Wx_OWM_Forecast_Temperature_Range_Day0
				Default item=Wx_OWM_Forecast_Temperature_Min_Day0 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Max_Day0 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]

				Default item=Wx_OWM_Forecast_Rain_Day0

				Default item=Wx_OWM_Forecast_Snow_Day0

				Default item=Wx_OWM_Forecast_Humidity_Range_Day0
				Default item=Wx_OWM_Forecast_Humidity_Min_Day0
				Default item=Wx_OWM_Forecast_Humidity_Max_Day0

				Default item=Wx_OWM_Forecast_Pressure_Range_Day0
				Default item=Wx_OWM_Forecast_Pressure_Min_Day0
				Default item=Wx_OWM_Forecast_Pressure_Max_Day0

				Default item=Wx_OWM_Forecast_Cloudiness_Range_Day0
				Default item=Wx_OWM_Forecast_Cloudiness_Min_Day0
				Default item=Wx_OWM_Forecast_Cloudiness_Max_Day0

				Default item=Wx_OWM_Forecast_Wind_Speed_Range_Day0
				Default item=Wx_OWM_Forecast_Wind_Speed_Min_Day0
				Default item=Wx_OWM_Forecast_Wind_Speed_Max_Day0
			}
			
			Frame item=Wx_OWM_Forecast_Time_24h label="Tomorrow [%1$tA, %1$tb %1$td]" icon="calendar" {
				Default item=Wx_OWM_Forecast_Temperature_Min_Day1 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Max_Day1 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Range_Day1

				Default item=Wx_OWM_Forecast_Rain_Day1

				Default item=Wx_OWM_Forecast_Snow_Day1

				Default item=Wx_OWM_Forecast_Humidity_Range_Day1
				Default item=Wx_OWM_Forecast_Humidity_Min_Day1
				Default item=Wx_OWM_Forecast_Humidity_Max_Day1

				Default item=Wx_OWM_Forecast_Pressure_Range_Day1
				Default item=Wx_OWM_Forecast_Pressure_Min_Day1
				Default item=Wx_OWM_Forecast_Pressure_Max_Day1

				Default item=Wx_OWM_Forecast_Cloudiness_Range_Day1
				Default item=Wx_OWM_Forecast_Cloudiness_Min_Day1
				Default item=Wx_OWM_Forecast_Cloudiness_Max_Day1

				Default item=Wx_OWM_Forecast_Wind_Speed_Range_Day1
				Default item=Wx_OWM_Forecast_Wind_Speed_Min_Day1
				Default item=Wx_OWM_Forecast_Wind_Speed_Max_Day1
			}

			Frame item=Wx_OWM_Forecast_Time_48h label="In 2 days [%1$tA, %1$tb %1$td]" icon="calendar" {
				Default item=Wx_OWM_Forecast_Temperature_Min_Day2 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Max_Day2 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Range_Day2

				Default item=Wx_OWM_Forecast_Rain_Day2

				Default item=Wx_OWM_Forecast_Snow_Day2

				Default item=Wx_OWM_Forecast_Humidity_Range_Day2
				Default item=Wx_OWM_Forecast_Humidity_Min_Day2
				Default item=Wx_OWM_Forecast_Humidity_Max_Day2

				Default item=Wx_OWM_Forecast_Pressure_Range_Day2
				Default item=Wx_OWM_Forecast_Pressure_Min_Day2
				Default item=Wx_OWM_Forecast_Pressure_Max_Day2

				Default item=Wx_OWM_Forecast_Cloudiness_Range_Day2
				Default item=Wx_OWM_Forecast_Cloudiness_Min_Day2
				Default item=Wx_OWM_Forecast_Cloudiness_Max_Day2

				Default item=Wx_OWM_Forecast_Wind_Speed_Range_Day2
				Default item=Wx_OWM_Forecast_Wind_Speed_Min_Day2
				Default item=Wx_OWM_Forecast_Wind_Speed_Max_Day2
			}

			Frame item=Wx_OWM_Forecast_Time_72h label="In 3 days [%1$tA, %1$tb %1$td]" icon="calendar" {
				Default item=Wx_OWM_Forecast_Temperature_Range_Day3
				Default item=Wx_OWM_Forecast_Temperature_Min_Day3 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Max_Day3 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Rain_Day3

				Default item=Wx_OWM_Forecast_Snow_Day3

				Default item=Wx_OWM_Forecast_Humidity_Range_Day3
				Default item=Wx_OWM_Forecast_Humidity_Min_Day3
				Default item=Wx_OWM_Forecast_Humidity_Max_Day3

				Default item=Wx_OWM_Forecast_Pressure_Range_Day3
				Default item=Wx_OWM_Forecast_Pressure_Min_Day3
				Default item=Wx_OWM_Forecast_Pressure_Max_Day3

				Default item=Wx_OWM_Forecast_Cloudiness_Range_Day3
				Default item=Wx_OWM_Forecast_Cloudiness_Min_Day3
				Default item=Wx_OWM_Forecast_Cloudiness_Max_Day3

				Default item=Wx_OWM_Forecast_Wind_Speed_Range_Day3
				Default item=Wx_OWM_Forecast_Wind_Speed_Min_Day3
				Default item=Wx_OWM_Forecast_Wind_Speed_Max_Day3
			}

			Frame item=Wx_OWM_Forecast_Time_96h label="In 4 days [%1$tA, %1$tb %1$td]" icon="calendar" {
				Default item=Wx_OWM_Forecast_Temperature_Range_Day4
				Default item=Wx_OWM_Forecast_Temperature_Min_Day4 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]
				Default item=Wx_OWM_Forecast_Temperature_Max_Day4 valuecolor=[
					>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
				]

				Default item=Wx_OWM_Forecast_Rain_Day4

				Default item=Wx_OWM_Forecast_Snow_Day4

				Default item=Wx_OWM_Forecast_Humidity_Range_Day4
				Default item=Wx_OWM_Forecast_Humidity_Max_Day4
				Default item=Wx_OWM_Forecast_Humidity_Min_Day4

				Default item=Wx_OWM_Forecast_Pressure_Range_Day4
				Default item=Wx_OWM_Forecast_Pressure_Min_Day4
				Default item=Wx_OWM_Forecast_Pressure_Max_Day4

				Default item=Wx_OWM_Forecast_Cloudiness_Range_Day4
				Default item=Wx_OWM_Forecast_Cloudiness_Min_Day4
				Default item=Wx_OWM_Forecast_Cloudiness_Max_Day4

				Default item=Wx_OWM_Forecast_Wind_Speed_Range_Day4
				Default item=Wx_OWM_Forecast_Wind_Speed_Min_Day4
				Default item=Wx_OWM_Forecast_Wind_Speed_Max_Day4
			}

		}
	}
	// Add your other home automation stuff
}

```

> [@LukaNoah](#):
>
> PS. Would not that be something for a direct implementation in the binding itself? You would save a lot of items …

I agree. And it would also be much more efficient in terms of resources.

---

<div class="post-metadata">

**Author:** ![LukaNoah](https://community.openhab.org/letter_avatar_proxy/v4/letter/l/bc79bd/32.png) [@LukaNoah](https://community.openhab.org/u/LukaNoah)\
**Post date:** [March 3, 2019, 9:58am UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/14 "2019-03-03T09:58:28Z")

</div>

Many Thanks. Now it works without errors.  
When I see with what effort you have to make a prediction now, that’s really insane, even the number of items required. And then the time it takes …  
It would be really useful to put everything into the binding.

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [March 3, 2019, 7:22pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/15 "2019-03-03T19:22:16Z")

</div>

> [@shutterfreak](#):
>
> effectively referring to 2 `Number:Speed` values rendered as `"[%.0f km/h - %.0f km/h]"` . But that’s a different story.

This probably won’t happen for sitemaps. This also has nothing to do with UoM. It’s a limitation of all Items.

The standard approach is to create a proxy String item and a rule to set that item using the states of the two or more items.

With HABpanel, one can create a custom widget to display the two or more values. There are some very impressive weather widgets in the gallery.

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [March 5, 2019, 9:19pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/16 "2019-03-05T21:19:32Z")

</div>

Here’s a way to get hold of the current day’s minimum and maximum temperature values (as measured since midnight). You can add the code provided below to the code I provided earlier in this thread.

**openweathermap.items** :

```csv
// You probably already defined the following Item (it's reproduced here for your convenience):
Number:Temperature Wx_OWM_Current_Temperature "Temperature [%.1f %unit%]"	<temperature>	(gWeatherCurrent) {channel="openweathermap:weather-and-forecast:77e8fd82:local:current#temperature"}
// The following 2 proxy items will be updated with a rule whenever a new OWM forecast is read (once per hour):
Number:Temperature Wx_OWM_Current_Temperature_Min "Temperature Low Today [%.1f %unit%]"	<temperature>
Number:Temperature Wx_OWM_Current_Temperature_Max "Temperature High Today [%.1f %unit%]"	<temperature>

```

**openweathermap.rules** :

```csv
/* Compute daily min and max values for OWM bindings up to 'now'
 */
 rule "Daily min/max values for OWM bindings"
when
    // Time cron "0/15 * * * * ?" or // DEBUG
    // Time cron "0 1 0 * * ?" or // Run at 00:01 daily (reset the daily min/max values)
    Item Wx_OWM_Current_Observation_Time changed 
then
    val String ruleTitle = "Test daily min/max values for OWM bindings"

    val min = Wx_OWM_Current_Temperature.minimumSince(now.withTimeAtStartOfDay).state
    val max = Wx_OWM_Current_Temperature.maximumSince(now.withTimeAtStartOfDay).state

    postUpdate(Wx_OWM_Current_Temperature_Min, min)
    postUpdate(Wx_OWM_Current_Temperature_Max, max)

    logInfo(ruleTitle, "Today's temperature Min - Max values = '{}' - '{}'", min, max)
end

```

**home.sitemap** :

```csv
// Current temperature:
Default item=Wx_OWM_Current_Temperature label="Temperature [%.1f %unit%]" valuecolor=[
	>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
]
// Today's lowest temperature reading so far:
Default item=Wx_OWM_Current_Temperature_Min valuecolor=[
	>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
]
// Today's highest temperature reading so far:
Default item=Wx_OWM_Current_Temperature_Max valuecolor=[
	>=30="red", >=25="orange", >=15="green", 0="silver", <0="purple", <15="blue"
]

```

The color of the value changes depending on the actual temperature value (I’m working with `°C`).

---

<div class="post-metadata">

**Author:** ![wimu](https://community.openhab.org/letter_avatar_proxy/v4/letter/w/e480ec/32.png) [@wimu](https://community.openhab.org/u/wimu)\
**Post date:** [June 15, 2019, 9:52am UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/17 "2019-06-15T09:52:21Z")

</div>

This looks great to me.

I tried to use the `openweathermap.items` and `openweathermap.rules` from above but I don’t get data in my sitemap.

In my former openweathermap installation I used the things and items from the original binding example.

In the example from Olivier I don’t see where to put my own API-key from openweathermap. Is there a `openweathermap.things` file needed? Do I have to replace “77e8fd82” in all items of `openweathermap.items`?

My Visual Studio editor also reported some problems in the openweathermap.rules file:  
The method or field Astro\_Sunset\_End is undefined  
The method or field Astro\_Sunrise\_Start is undefined  
The method or field Astro\_Sun\_Elevation is undefined  
My Astro.items only know “Sunrise\_Time” and “Sunset\_Time”. What goes wrong here?

Kind regards,

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [June 15, 2019, 10:46am UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/18 "2019-06-15T10:46:00Z")

</div>

> [@wimu](#):
>
> In the example from Olivier I don’t see where to put my own API-key from openweathermap. Is there a `openweathermap.things` file needed? Do I have to replace “77e8fd82” in all items of `openweathermap.items` ?

The **OWM API key** should be configured in Paper UI by editing the following `Thing`: OpenWeatherMap Account.

You should indeed replace `“77e8fd82”` everywhere with the ID reported by your OWM environment. Again, by looking at one of the following `Thing`s:

- OpenWeatherMap Account
- Local weather and forecast

> [@](#):
>
> My Visual Studio editor also reported some problems in the openweathermap.rules file:  
> The method or field Astro\_Sunset\_End is undefined  
> The method or field Astro\_Sunrise\_Start is undefined  
> The method or field Astro\_Sun\_Elevation is undefined  
> My Astro.items only know “Sunrise\_Time” and “Sunset\_Time”. What goes wrong here?

I forgot to add the astro items:

```auto
DateTime Astro_Sunrise_Start "Sunrise Start" <sunrise> (AstroSun)	{ channel="astro:sun:local:rise#start" }
DateTime Astro_Sunrise_End "Sunrise End" <sunrise> (AstroSun)	{ channel="astro:sun:local:rise#end" }

DateTime Astro_Noon_Start "Noon Start" <sun> (AstroSun)	{ channel="astro:sun:local:noon#start" }
DateTime Astro_Noon_End "Noon End" <sun> (AstroSun)	{ channel="astro:sun:local:noon#end" }

DateTime Astro_Sunset_Start "Sunset Start" <sunset> (AstroSun)	{ channel="astro:sun:local:set#start" }
DateTime Astro_Sunset_End "Sunset End" <sunset> (AstroSun)	{ channel="astro:sun:local:set#end" }

Number:Angle	Astro_Sun_Azimuth "Azimuth" <incline> (AstroSun)	{ channel="astro:sun:local:position#azimuth" }
Number:Angle	Astro_Sun_Elevation "Elevation" <incline> (AstroSun)	{ channel="astro:sun:local:position#elevation" }

```

---

<div class="post-metadata">

**Author:** ![wimu](https://community.openhab.org/letter_avatar_proxy/v4/letter/w/e480ec/32.png) [@wimu](https://community.openhab.org/u/wimu)\
**Post date:** [June 15, 2019, 7:33pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/19 "2019-06-15T19:33:22Z")

</div>

Hi Olivier,  
Thanks for your answer.

I did replace “77e8fd82” with my own API-key in the whole `openweathermap.items` file. (577 replacements?). But my API-key is 32 characters long?!? Yours is 8!

In Paper UI I - “Openweathermap account” can’t read the my API-key (characters hidden). When I try to edit my “Openweathermap account” (editting my Location name) in Paper UI and I save my settings I get a short message “Thing updated” and “Error 409”. When I don’t change anything but only save my settings I only get a message “Thing updated”. The “Status” in my Paper UI - Openweathermap account always is “Online”(green).

Should the API-key be replaced in other files too?

I added the `astro.items` to my file and they seem to work now.

Regards.

---

<div class="post-metadata">

**Author:** ![shutterfreak](https://community.openhab.org/user_avatar/community.openhab.org/shutterfreak/32/74067_2.png) [@shutterfreak](https://community.openhab.org/u/shutterfreak)\
**Post date:** [June 15, 2019, 10:44pm UTC](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513/20 "2019-06-15T22:44:29Z")

</div>

> [@wimu](#):
>
> I did replace “77e8fd82” with my own API-key in the whole `openweathermap.items` file. (577 replacements?). But my API-key is 32 characters long?!? Yours is 8!

It’s not your API key but the Thing ID of your OWM binding. You can see the Thing ID for any thing defined when opening the thing overview in Paper UI at http://[your OpenHAB server name or IP address goes here]:8080/paperui/index.html#/configuration/things - look for the Thing ID in the overview, search “openweathermap” to narrow the set of things. It’s the 8-character identifier you find in all 3 items (the binding, the weather items and the UV items).

[Next page](https://community.openhab.org/t/openweathermap-daily-forecasts-with-free-api-using-plain-openhab-rules/68513.md?page=2)
