L'API météorologique de Yahoo en Free Pascal
2018-02-17
Spoken Weather Forecasts and Internet Radio on the Orange Pi Zero with Armbian

Les prévisions météorologiques que j'utilisais dans un billet antérieur ont le défaut de leur qualité. Très précises pour ma localité, elles sont peu utiles pour d'autres situés ailleurs que dans la province. En réalité, les textes de prévisions publiques du ministère de l'Environnement du Canada ne se prêtent pas à une consultation ponctuelle, pour un quelconque endroit au Canada. De plus il n'y a rien pour tout endroit à l'extérieur du pays.

Parmi d'autres sites semblables, le vénérable Yahoo! offre une interface de programmation d'applications (IPA ou API en anglais) pour les prévisions météorologiques ayant quatre belles qualités. Sa couverture géographique est assez bonne. L'accès est libre sans nécessiter un enregistrement pour obtenir une clé. On peut spécifier l'endroit visé par son nom et, éventuellement, sa région ou son pays s'il y a possibilité d'ambiguïté. Vous saviez qu'il y a un Paris à moins d'une heure de Stratford en Ontario (Canada) ? Enfin, les données sont fournies dans un fichier au format JSON que je trouve bien plus simple que le XML.

J'ai écrit une petite unité en Pascal (Free Pascal) qui forme l'URL de la requête HTTPS en fonction des données météorologiques désirées et qui récupère les résultats auprès du serveur de Yahoo!. Voici l'essentiel de la partie interface de l'unité nommée yw.pas.

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;

Trois types sont déclarés dont TYahooWeatherErr pour les exceptions qui pourraient se produire avec la fonction principale YahooWeatherJSONData qu'on verra plus loin.

Le type TWeatherElements (attention au «s») dénote un ensemble de type TWeatherElement. Chaque membre de cette énumération identifie des données à récupérer. Les plus importantes sont weCondition et weForecast pour obtenir, respectivement, les conditions actuelles et les prévisions pour la journée courante et les neuf prochains jours. Puisqu'il peut y avoir une méprise sur l'endroit, il est préférable de toujours obtenir les données identifiant l'endroit en ajoutant weLocation. Aussi bien aussi demander quelles sont les unités de mesure, car les données ne sont que des valeurs numériques. Avec weWind on obtient la vitesse et la direction du vent ainsi que l'effet éolien, avec wdAtmosphere c'est l'humidité, la pression barométrique et la visibilité actuelle qui sont fournis. Les heures de lever et de coucher de soleil sont récupérées avec weAstronomy (heure locale).

La fonction YahooWeatherUrl formule et renvoie la requête URL à soumettre à Yahoo! pour obtenir les éléments météorologiques voulus. Les paramètres de la fonction sont

location nom de la localité et facultativement sa région et son pays (ex «Paris, On» - pour Paris, Ontario, Canada).
On peut aussi mettre les coordonnées d'un endroit (ex «(40.7141667,-74.0063889)» pour New York, New York)
elements ensemble d'éléments météorologiques souhaités à inclure dans la sortie (par défaut: [weLocation, weCondition]]).
celsius mettre à vrai (true) pour que les mesures soient en unités du Système International,
mettre à faux (false) pour les mesures soient en unités impériales (qui ne sont guère utilisés ailleurs qu'aux États-Unis)
(par défaut: true - mesures SI)
json mettre à vrai (true) pour que la sortie soit au format JSON,
mettre à faux (false) pour que la sortie au format XML,
(par défaut: true - JSON)

La fonction YahooWeatherRawData renvoie les données brutes obtenues de Yahoo! Weather. Si une erreur survient, la chaîne renvoyée sera un message d'erreur qui commence par le mot «ERROR» ou son équivalent traduit.

Ces deux fonctions sont utilisées par la fonction principale de l'unité, YahooWeatherJSONData, qui transforme le texte brut en objet TJSONData. Si une erreur se produit, une exception de type TYahooWeatherErr est soulevée avec comme message la chaîne retournée par la fonction YahooWeatherRawData. Il appartient à l'appelant de détruire l'objet lorsqu'il n'est plus nécessaire. Ainsi l'utilisation typique de la fonction ressemble à ceci.

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;

Pour simplifier l'utilisation de l'objet TJSONData obtenu avec la fonction précédente, une quatrième fonction est fournie : GetWeatherElement. Cette fonction prend comme arguments Wanted un élément de météo désirée et src l'objet TJSONData pour retourner une valeur de type TJSONObject dans la variable elem qu'on utilise pour récupérer les données associées à l'élément. Puisque c'est un objet, il s'agit en réalité d'un pointeur vers les données qui appartiennent à l'objet src de type TJSONData. Il ne faut pas détruire l'objet elem car cela causerait une exception lors de la destruction de src. Si l'élément n'est pas présent, la fonction renvoie la valeur faux. On peut donc utiliser cette fonction pour récupérer les données pour un élément qui n'avait pas été demandé sans provoquer d'erreur. Conséquemment, il n'est pas nécessaire de préservé la valeur du paramètre elements de la fonction YahooWeatherJSONData et l'on peut procéder comme ci-dessous.

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;

Il est aussi possible d'utiliser le champs Strings de TJSONObject si l'on ne veut pas modifier le format d'affichage des nombres.

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;

La fonction TForm1.WeatherText dans l'unité main.pas de l'application yweather montre quels sont tous les sélecteurs qu'on peut utiliser avec chaque élément météorologique. D'ailleurs voici comment se présente ce programme.

Ce n'est pas très joli j'en conviens, mais il faut comprendre que le but est de transformer les données obtenues du Web en un texte pouvant être dit par un programme de synthèse vocale. Ce qu'on peut voir est un bon début, mais il reste encore à faire. Les heures au format hh:mm, les degrés °C, la vitesse km/h sont compris par pico tts. En revanche, l'abréviation mb n'est pas prise en charge, ni les directions comme SO, E et ainsi de suite. De plus, les parenthèses ne font pas très bien quand transformées en son.

La documentation indique que la pression est mesurée en «psi» (livres par pouce carré) lorsque les mesures impériales (américaines) sont choisies. C'est faux, c'est évidemment des pouces de mercure («in Hg») comme sur la page météo de Yahoo. Mais il y a plus. Regardez la pression barométrique sur la capture d'écran ci-dessus, elle est clairement bancale. Une pression supérieure à 33 000 millibars est environ 34 fois ce qu'elle devrait être. Si les mesures impériales sont choisies, la valeur retournée sera de nouveau 34 fois supérieure à ce qu'elle devrait être. Il semble qu'un facteur de conversion supplémentaire (1 pouce Hg = 33,8639 mb) a été introduit dans le calcul des valeurs. Le problème est connu depuis au moins un an comme l'atteste ces deux discussions sur des forums Yahoo weather API returning bad value for pressure and Barometric pressure reading seems wrong. Aucune correction n'a encore été appliquée. Le fichier source yw.pas contient une directive, FIX_PRESSURE, utilisée pour activer une correction simple. Si, à l'avenir, les valeurs de pression sont trop faibles (environ 34 fois moins élevées que ce qu'elles devraient être), éliminez la directive, car cela signifie que Yahoo a finalement corrigé le problème.

Cette lenteur à apporter une correction mineure, l'état délabré du site des développeurs (manque d'information, liens vers des pages qui n'existent plus...) et l'incertitude quant à l'avenir de Yahoo depuis son achat par Verizon n'inspirent pas confiance. Le service sera-t-il toujours en place dans quelques mois ? Voici une liste de remplacements possible pour le service Yahoo! Weather avec les limites du nombre de requêtes en une minute, une heure ou une journée pour conserver un accès gratuit. Notez toutefois qu'il faut s'inscrire auprèsse de chaqu'un de ces services pour obtenir une clé qu'il faut ajouter à toute requête.

Limite de requêtes
Non Lien minute heure jour
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  

Je pourrai tester le service de prévision Dark Sky (anciennement forecast.io) à l'avenir puisque j'ai déjà un compte auprès de ce fournisseur. D'ailleurs ce compte est sollicité à toutes les cinq minutes par le serveur de domotique.

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)

Le journal de Domoticz semble indiquer qu'il y a six requêtes séparées à chaque 5 minutes. Or la limite quotidienne serait dépassée à ce rythme. Donc il ne doit y avoir qu'une seule requête au cinq minutes que Domoticz scinde en six ce qui correspond aux six dispositifs associés à Dark Sky créés par l'application et affichés dans l'onglet Météo. Il serait possible de faire jusqu'à 712 de demandes ponctuelles de prévisions par jour sans dépasser les limites pour l'utilisation gratuite.

Références

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 de Hans Luijten
https://www.tweaking4all.com/software-development/lazarus-development/yahoo-weather-forecast/

 

Téléchargement

L'archive yweather.zip contient le code source de l'application yweather comme exemple d'utilisation de l'unité yw.pas.
Spoken Weather Forecasts and Internet Radio on the Orange Pi Zero with Armbian