OpenWeatherMap daily forecasts with free API using plain OpenHAB rules

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:

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.

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.

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.

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 …

Here you are:

  1. openweathermap.items.txt (118.5 KB) → please save as $OPENHAB_CONF/items/openweathermap.items after downloading
  2. openweathermap.rules.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:

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:

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:

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
}

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

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.

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.

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:

// 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:

/* 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:

// 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).

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,

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 Things:

  • OpenWeatherMap Account
  • Local weather and forecast

I forgot to add the astro items:

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" }

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.

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).

I created my a openweathermap.things myself (I come from Openhab 1.x and was used to create alle files myself at that time). Things became visible in Paper UI like you described however without Thing ID.

Now removed my Thing-file, Things in Paper UI and the openweathermap binding. Restarted my system and installed the openweathermap binding from scratch. Created new things:

Openweathermap

The thing ID of both things is not the same. Is that correct?
I used the thing ID from the Weather And Forecast thing.

I do have Current Weather data in my openweathermap.sitemap (a temporary sitemap copied from your example). But no Forecast nor Graphs with data.

It’s weird that both Thing IDs are different. Are you sure that you didn’t use/define the OWM account elsewhere?

Please use the Thing ID ending in “d5” and report.

I already did and “d5” didn’t work at all.

I deleted and recreated the things for another time but they don’t work anymore. Today i don’t have that much time. First gonna try to create them again with the same Thing ID.

Is it possible to send a printscreen so I can compare the exact definition/description with yours? Of course with blanked ID.

Mayby I have tot remove items and sitemap too?
Could it be wise to clear logs too? How to do that? Just delete the log-files?

Did you find a solution in the meantime ?

My owm.things look like:

Bridge openweathermap:weather-api:api "OpenWeatherMap Account" [apikey="your api-key from OWM 32 char", refreshInterval=30, language="de"] {
     Thing weather-and-forecast local "Local Weather And Forecast" [location="xx.xxxxxx,y.yyyyyy,zzz", forecastHours=120, forecastDays=0]
     Thing uvindex local "Local UV Index" [location="xx.xxxxx,y.yyyyyyyy", forecastDays=7]
}

and an item with the corresponding Thing-Channel (weather-and-forecast) looks like:

Number:Temperature   localCurrentTemperature                 "Aktuelle Temperatur [%.1f %unit%]"                            <temperature> (gOWM)  { channel="openweathermap:weather-and-forecast:api:local:current#temperature" }
Number:Temperature   localHourlyForecast3Temperature         "Temperatur in 03 Stunden[%.1f %unit%]"                        <temperature> (gOWM)  { channel="openweathermap:weather-and-forecast:api:local:forecastHours03#temperature" }
Number:Temperature   localHourlyForecast6Temperature         "Temperatur in 06 Stunden [%.1f %unit%]"                       <temperature> (gOWM)  { channel="openweathermap:weather-and-forecast:api:local:forecastHours06#temperature" }


and for an item for uv-index it then looks like:

Number               localCurrentUVIndex                     "Aktueller UV Index [%d]"                                         <uv_index>     (gOWM)  { channel="openweathermap:uvindex:api:local:current#uvindex" }
Number               localTomorrowUVIndex                    "Morgen UV Index [%d]"                                            <uv_index>     (gOWM)  { channel="openweathermap:uvindex:api:local:forecastTomorrow#uvindex" }


Hope this can help you.
Cheers,
Peter

EDIT:


The red marked Part is what you can copy in your item-channel-information.

owm

API key for free API configured in “OpenWeatherMap Konto”.
Both OWM things seem to be ok (Online).
I downloaded both files, copied the sitemap and replaced the “77xxxx” in the item file.

No data / values show in the sitemap…


How can I troubleshoot this?

some success… I forgot to remove “local” from the channels in the item file, since my things weren’t created manually.

Now “Current weather” has data!

Still, NO data in “Forecast”… ???

Forecast data appeared after some time as well !!