Using DWD as weather forecast provider

As i was not happy with the data-quality of owm for my region, i decided to to try out DWD (Deutscher Wetterdienst).

A quick search ended without suitable ended with the result, that there are only bindings for warnings available, but none for forecast.

So here is my solution:

Gathering the data directly from DWD is pretty complex, despite of their open data project. Luckily there is Bright Sky (https://brightsky.dev/), who are transforming the DWD data to a json output, which could be used in a rule.

For using the rule you need to have the following bindings installed:

  • http

  • jsonpath transofrmation

the consits of two parts, generating a http string for requesting the data and transforming the data to items.

The rules-File

rule "Bright Sky"
when
	Time cron "0 */30 * * * ?"
then
	var String lat = "1"		//set latitude
	var String lon = "3.14"		//set longitude

	//Get Time Data
	var String startDate = now().format(java.time.format.DateTimeFormatter.ofPattern("YYYY-MM-dd'T'HH'%3A'mmZ"))
	var String endDate = now().plusHours(24).format(java.time.format.DateTimeFormatter.ofPattern("YYYY-MM-dd'T'HH'%3A'mmZ"))
	
	//Generate URL
	startDate = "date=" + startDate.substring(0,21)
	startDate = startDate.replace("+","%2B") + "%3A00&"
	
	endDate = "date=" + endDate.substring(0,21)
	endDate = endDate.replace("+","%2B") + "%3A00&"
	lat = "lat=" + lat + "&"
	lon = "lon=" + lon
	val String baseurl = "https://api.brightsky.dev/weather?"
	val String WeatherString = sendHttpGetRequest(baseurl + startDate + endDate + lat + lon)

	//Writing temperature Data for the next 14 hours into corresponding items
	var Number i = 0
	while (i < 15) {
		var Number Forecast = Float::parseFloat(transform("JSONPATH", "$.weather[" + i + "].temperature", WeatherString))
		var String DestItem = "WeatherForecast_Temp" + i
		sendCommand(DestItem, Forecast.toString)
		i = i + 1
	}
	
	//writing wind speed, cloud cover an precipitation for the next 3 hours in the corresponding items
	i = 0
	while (i < 4){
		val Number Forecast1 = Float::parseFloat(transform("JSONPATH", "$.weather[" + i + "].wind_speed", WeatherString))
		val String DestItem1 = "WeatherForecast_Wind" + i
		sendCommand(DestItem1, Forecast1.toString)
		
		val Number Forecast2 = Float::parseFloat(transform("JSONPATH", "$.weather[" + i + "].cloud_cover", WeatherString))
		val String DestItem2 = "WeatherForecast_Clouds" + i
		sendCommand(DestItem2, Forecast2.toString)
		
		val Number Forecast3 = Float::parseFloat(transform("JSONPATH", "$.weather[" + i + "].precipitation", WeatherString))
		val String DestItem3 = "WeatherForecast_Rain" + i
		sendCommand(DestItem3, Forecast3.toString)

		i = i + 1
	}
end

with the corresponding items:

Number WeatherForecast_Temp0 "Aktuelle Temperatur [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp1 "Vorhergesagte Temperatur 1h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp2 "Vorhergesagte Temperatur 2h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp3 "Vorhergesagte Temperatur 3h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp4 "Vorhergesagte Temperatur 4h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp5 "Vorhergesagte Temperatur 5h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp6 "Vorhergesagte Temperatur 6h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp7 "Vorhergesagte Temperatur 7h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp8 "Vorhergesagte Temperatur 8h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp9 "Vorhergesagte Temperatur 9h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp10 "Vorhergesagte Temperatur 10h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp11 "Vorhergesagte Temperatur 11h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp12 "Vorhergesagte Temperatur 12h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp13 "Vorhergesagte Temperatur 13h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)
Number WeatherForecast_Temp14 "Vorhergesagte Temperatur 14h [%.1f °C]" (gWeatherForecast, gWForecast_Temp)

Number WeatherForecast_Wind0 "Aktuelle Windgeschwindigkeit [%.1f km/h]" (gWeatherForecast, gWForecast_Wind)
Number WeatherForecast_Wind1 "Vorhergesagte Windgeschwindigkeit 1h [%.1f km/h]" (gWeatherForecast, gWForecast_Wind)
Number WeatherForecast_Wind2 "Vorhergesagte Windgeschwindigkeit 2h [%.1f km/h]" (gWeatherForecast, gWForecast_Wind)
Number WeatherForecast_Wind3 "Vorhergesagte Windgeschwindigkeit 3h [%.1f km/h]" (gWeatherForecast, gWForecast_Wind)

Number WeatherForecast_Clouds0 "Aktuell Bewölkung [%.2f p]" (gWeatherForecast, gWForecast_Clouds)
Number WeatherForecast_Clouds1 "Vorhergesagte Bewölkung 1h [%.2f p]" (gWeatherForecast, gWForecast_Clouds)
Number WeatherForecast_Clouds2 "Vorhergesagte Bewölkung 2h [%.2f p]" (gWeatherForecast, gWForecast_Clouds)
Number WeatherForecast_Clouds3 "Vorhergesagte Bewölkung 3h [%.2f p]" (gWeatherForecast, gWForecast_Clouds)

the rules is asking the data for the next 24 hours and writing the temperature data for the next 14 hours, and the wind, cloudines and precipitation data for the next 3 hours in the corresponding items.

For usage you have to set your geolocation (longitude/latitude).

More datapoints are documented at the bright sky homepage.

At the moment distingusihing between summer an winter time has to be done manually with the UTCoffset variable (02 for summer, 01 for winter).

Maybe it’s useful for someone :slight_smile:

Edit:
Update the rule for automatic switching between summer/winter time according to the idea of Matze0211.

Greets

Alex

3 Likes

I’m using Meteograms to show my local forecast. You can select the providerof the data , see my actual meteogram:

Meteograms seems to be mainly for visualization?
The intend of the rule was, to make the data available for further processing. In may case, i use forecast temperatures and cloudiness to determine whether to open or close the blinds/rollershutters.

Correct!

If you have Node-Red, you can use DWDweather node, which does all things for you. I use it for years already.

Thanks for sharing this.

In regards to the timezone / UTC offset, shouldn’t the Z argument be enough and the DateTimeFormatter will automatically append the timezone offset to the date string?

Also is there any specific reason why you use a rule for this and not directly use a http thing + json transformation and add the relevant channels to the thing?

With a given time, Bright Sky expects to get the correct time zone, at least it is shown in their example:

2020-04-24T12:00+02:00

https://brightsky.dev/docs/#get-%2Fweather=

As far as i know, it’s not possible to create an dynamic url in a things file, which is needed to provide the current date+time.

Edit:
I think i got you wrong in regards of the timezone string:
For the URL you need the proper escape codes, so the string

+02:00

is actually

%2B02%3A00

Is this also possible with the DateTimeFormatter?

Within the http binding it’s in general possible to dynamically append date & time information to the URL.

However I think you are right, that in regards to the timezone the + is not correctly escaped, as without the timezone (just adding date & time to the url) the request will work, but including the timezone the request will not work

you can append a Z to your date time pattern

change

YYYY-MM-dd'T'HH'%3A'mm

to

YYYY-MM-dd'T'HH'%3A'mmZ

and than replace the + with an %2B

change

startDate = "date=" + startDate + "%2B" + UTCoffset + "%3A00&"

to

startDate = "date=" + startDate.replace("+","%2B") + "&"

what would at least remove the need to manually set the timezone offset.

Not sure if the issue with adding the timezone directly within the HTTP thing would be resolved in OH3.3, as I am still with OH3.2

Nice idea thanks! Only just adding the Z did not work for me, as the result was

+0200

instead of

+02:00

So i added

startDate = "date=" + startDate.substring(0,21)

to cut the two last characters and add the “%3A00&” later on. Might be a bit clumsy as only full hours are working, but sufficient as this might be mostly used in germany.

First post is edited accordingly.