The Yahoo! Weather API with Free Pascal
2018-02-17
Spoken Weather Forecasts and Internet Radio on the Orange Pi Zero with Armbian

The weather forecasts I described in a previous post have the advanage of being very specific. However, they are not very useful for others located outside of my local area. In fact, the Environment Canada public forecasting texts do not lend themselves to ad hoc consultation for any arbitrary place in Canada. Of course, they are useless for any place outside the country.

Among other similar sites, the venerable Yahoo! offers an application programming interface (API) for weather forecasts with four good qualities. Its geographical coverage is quite good. Access is free with no registration requirement to obtain a key. It is possible to specify the location by its name and possibly its region or country if there is a possibility of ambiguity. Did you know that Paris is less than an hour from Stratford in Ontario (Canada)? Finally, the data is provided in a file in JSON format that I find much simpler than XML.

I wrote yw.pas, a small unit in Pascal (Free Pascal) that formulates the HTTPS request based on the desired meteorological data and retrieves the results from the Yahoo! Server. Here is the main part of the interface part of the unit.

type TYahooWeatherErr = class(Exception); TWeatherElement = (weLocation, weUnits, weCondition, weWind, weAtmosphere, weAstronomy, weForecast); TWeatherElements = set of TWeatherElement; function YahooWeatherUrl(const location: string; elements: TWeatherElements = [weLocation, weCondition]; celsius: boolean = true; json: boolean = true): string; function YahooWeatherRawData(const location: string; elements: TWeatherElements = [weLocation, weCondition]; celsius: boolean = true; json: boolean = true): string; function YahooWeatherJSONData(const location: string; elements: TWeatherElements = [weLocation, weCondition]; celsius: boolean = true): TJSONData; function GetWeatherElement(Wanted: TWeatherElement; src: TJSONData; var elem: TJSONObject): boolean;

Three types are declared including TYahooWeatherErr for the exceptions that could occur with the main function YahooWeatherJSONData that we will see later.

The TWeatherElements (note the final "s") type denotes a set of TWeatherElement. Each member of this enumeration identifies data to retrieve. The most important are weCondition and weForecast to obtain, respectively, the current conditions and forecasts for the current day and the next nine days. Since there may be a misunderstanding about the location, it is best to always get the data identifying it by adding weLocation. The units of measurement should also be requested with weUnits because the data are just numeric values. With weWind, we get the wind speed and direction as well as the wind effect, with wdAtmosphere it is humidity, barometric pressure and current visibility that are provided. Sunrise and sunset times are collected with weAstronomy (local time).

The YahooWeatherUrl function returns the HTTP request to submit to Yahoo! to obtain desired meteorological data. The parameters of the function are:

location name of the locality and optionally its region and country (eg "Paris, On" - for Paris, Ontario, Canada).
We can also put the coordinates of a place (ex "(40.7141667, -74.0063889)" for New York, New York)
elements set of desired meteorological elements to include in the output (default: [weLocation, weCondition]).
celsius set to true for measurements in International System units,
set to false for measurements in imperial units (which are hardly used anywhere else than in the United States)
(default: true - SI measures)
json set to true for the output to be in JSON format,
set to false so that the output in XML format, (default: true - JSON)

The YahooWeatherRawData function returns the raw data obtained from Yahoo! Weather. If an error occurs, the returned string will be an error message that begins with the word "ERROR" or its translated equivalent.

Both of these functions are used by the unit's main function, YahooWeatherJSONData, which turns plain text into a TJSONData object. If an error occurs, a TYahooWeatherErr exception is raised with the message returned by the YahooWeatherRawData function. Destruction of the ojbect when no longer needed is up to the caller. So the typical use of the function looks like this.

procedure TForm1.TypicalUse; var jdata: TJSONData; begin memo1.lines.clear; jdata := YahooWeatherJSONData(locationEdit.text, [weLocation, weUnits, weCondition, weForecast]); try memo1.lines.Text := jdata.AsJSON; finally jdata.free; end; end;

To simplify the use of the TJSONData object obtained with the previous function, a fourth function is provided: GetWeatherElement. This function takes as arguments Wanted a desired weather element and src the TJSONData object to return a TJSONObject in the variable elem variable that the caller can use to retrieve the data associated with the element. Since it is an object, it is actually a pointer to the data belonging to the object src of type TJSONData. Do not destroy the elem object as this would cause an exception when destroying src. If the element is not present, the function returns false. This function can be used to retrieve data for an element that was not requested without causing an error. Consequently, it is not necessary to preserve the value of the elements parameter of the function YahooWeatherJSONData. One can proceed as below.

procedure TForm1.PrintAtmosphere(jdata: TJSONData); var elem: TJSONObject; begin if getWeatherElement(weAtmosphere, jdata, elem) then begin memo1.lines.Add(Format('Humidité : %d%%', [elem.Integers['humidity']])); memo1.lines.Add(Format('Pression atmosphérique : %.0f mb', [elem.Floats['pressure']])); memo1.lines.Add(Format('Visibilité : %.1f km', [elem.Floats['visibility']])); end; end;

The Strings property of TJSONObject could be used if there is no need to modify the displayed numerical values.

procedure TForm1.PrintAtmosphere(jdata: TJSONData); var elem: TJSONObject; begin if getWeatherElement(weAtmosphere, jdata, elem) then begin memo1.lines.Add(Format('Humidité : %s%%', [elem.Strings['humidity']])); memo1.lines.Add(Format('Pression atmosphérique : %s mb', [elem.Strings['pressure']])); memo1.lines.Add(Format('Visibilité : %s km', [elem.Strings['visibility']])); end; end;

The TForm1.WeatherText function in unit main.pas of the yweather application shows all the selectors that can be used with each meteorological element. By the way, here is a screen shot of the program in action.

It's not very pretty I agree, but please make allowances for the goal of this exercise which is to transform the data obtained from the Web into a text that can be said by a speech synthesis program. What we can see is a good start, but there is still more to do. The hours in the format hh:mm, the degrees °C, the speed km/h are understood by pico tts. On the other hand, the abbreviation mb is not supported, nor the directions like SW, E and and so on. Moreover, parentheses do not tranlate well into sound.

According to the documentation, pressure is measured in psi (pounds per square inch) when imperial measures are requested. This is not correct, it is obvioulsy measured in inches of mercury («in Hg») as shown on the Weather page of Yahoo. But there is more. Look at the barometric pressure on the screenshot above, it is clearly wonky. Pressure over 33,000 millibars is about 34 times what it should be. If imperial measures are chosen, the returned value will again be 34 times greater than it should be. It appears that an additional conversion factor (1 inch Hg = 33.8639 mb) was introduced in the calculation of the values. The problem has been known for at least a year as evidenced by these two discussions on forums Yahoo weather API returning bad value for pressure and Barometric pressure reading seems wrong. Since a correction does not seem to be forthcoming, unit yw.pas contains the FIX_PRESSURE directive used to activate a rather unsophisticated fix. In the future, should the pressure values appear to be way too low (about 34 times lower than they should be), eliminate the directive because it means that Yahoo finally corrected the problem.

This delay in making a minor correction, the dilapidated state of the developer site (lack of information, links to pages that no longer exist ...) and uncertainty about the future of Yahoo since its purchase by Verizon do not inspire confidence. Will the service still be in place in a few months? Here is a list of possible replacements for the Yahoo! Weather along with the maximum number of queries in one minute, one hour or one day that must not be exceeded to maintain free access. Note, however, that you must obtain a key from each of these services that must be added to requests.

Maxmimu requests per
Name Address minute hour day
OpenWeatherMap http://www.openweathermap.com/ 60    
AccuWeather https://developer.accuweather.com     50
WeatherUnderground https://www.wunderground.com/ 10   500
Dark Sky https://darksky.net/dev/     1000
Apixu https://www.apixu.com/     1000
Weatherbit.io https://www.weatherbit.io/   75  

I will be able to test the Dark Sky forecasting service (formerly forecast.io) in the future since I already have an account with this provider. My home automation server gets data from this account every five minutes.

2018-02-17 12:01:53.370 (Dark Sky Weather Forecast) Temp + Humidity + Baro (THB) 2018-02-17 12:01:53.418 (Dark Sky Weather Forecast) Wind (Wind) 2018-02-17 12:01:53.459 (Dark Sky Weather Forecast) UV (UV Index) 2018-02-17 12:01:53.506 (Dark Sky Weather Forecast) Rain (Rain) 2018-02-17 12:01:53.548 (Dark Sky Weather Forecast) General/Visibility (Visibility) 2018-02-17 12:01:53.588 (Dark Sky Weather Forecast) General/Solar Radiation (Solar Radiation) 2018-02-17 12:06:54.026 (Dark Sky Weather Forecast) Temp + Humidity + Baro (THB) 2018-02-17 12:06:54.069 (Dark Sky Weather Forecast) Wind (Wind) 2018-02-17 12:06:54.109 (Dark Sky Weather Forecast) UV (UV Index) 2018-02-17 12:06:54.155 (Dark Sky Weather Forecast) Rain (Rain) 2018-02-17 12:06:54.197 (Dark Sky Weather Forecast) General/Visibility (Visibility) 2018-02-17 12:06:54.240 (Dark Sky Weather Forecast) General/Solar Radiation (Solar Radiation)

The Domoticz log seems to indicate that there are six separate queries every 5 minutes. But the daily limit would be exceeded at this rate. So there must be only one big query every five minutes that Domoticz splits in six which matches the six devices associated with Dark Sky created by the application and displayed in the Weather tab. It would be possible to make up to 712 ad hoc forecast requests per day without exceeding the limits for free use.

References

Yahoo Weather API for your apps
https://developer.yahoo.com/weather/

YQL at Yahoo! Developer Network
https://developer.yahoo.com/yql/guide/

Lazarus Pascal - Retrieve Yahoo Weather Forecast by Hans Luijten
https://www.tweaking4all.com/software-development/lazarus-development/yahoo-weather-forecast/

Download

The yweather.zip archive contains the source code for the yweather program demonstrating the use of the yw.pas unit.

Spoken Weather Forecasts and Internet Radio on the Orange Pi Zero with Armbian