Connaître la météo en Powershell (part 2)

C’est bien d’avoir les observations d’une station météo à un instant donné, mais quand on regarde Evelyne Dhéliat, ce n’est pas pour savoir le temps qu’il fait (il suffit de regarder par la fenêtre ou d’exécuter le script du premier article !) mais pour savoir le temps qu’il fera plus tard ! Et ça tombe bien, wunderground nous fournit des prévisions ! Voyons tout cela…

Vous vous souvenez de la variable $features en début de script ? Je vous avais dit qu’elle contenait les fonctionnalités désirées parmi ce que propose Wunderground. Pour l’instant nous nous sommes contenté des « conditions« , autrement dit des dernières observations de la station météo. C’est cette variable que nous allons donc modifier, pour obtenir encore plus d’infos dans notre fichier XML, infos qu’il faudra évidemment traiter.

Forecast

Pour avoir les prévisions, et bien c’est très simple, il s’agit d’ajouter la fonctionnalité « forecast » (prévision) à notre variable $features :

$features="conditions/forecast"

Avouez que ce n’est pas bien dur. Du coup, notre URL une fois générée va ressembler à ceci :

https://api.wunderground.com/api/1234567890123456/conditions/forecast/q/France/Cergy.xml

On se retrouve donc avec un nœud forecast dans notre fichier XML, ayant évidemment tout un tas de nœuds enfants :

Chouette, des prévisions météo ! Plus besoin d’Evelyne Dhéliat !

Bon alors déjà, premier problème : comme vous pouvez le voir, le texte est en anglais. Et si vous ne comprenez rien à l’anglais, ça ne va pas vous être très utile. Mais pas de panique, il y a une solution : il suffit de modifier notre variable $features et de préciser la langue que l’on souhaite (ici, le français tant qu’à faire) :

$features="conditions/forecast/lang:FR"

Et magie de l’informatique :

Ah ben déjà les francophones apprécieront !

Bon, premier problème réglé. Mais il y en a un second (argh !) : si vous regardez le nœud forecastdays (avec un s), il est parent de plusieurs nœuds forecastday (sans s)… ce qui veut dire qu’il va falloir faire une petite boucle pour tous les afficher. Ce qui nous intéresse particulièrement dans un des nœuds forecastday (sans s), ce sont :

  • icon_url : l’adresse d’une jolie icône que l’on pourra afficher lorsque notre application ne sera plus en mode console (autrement dit, quand nous aurons fait un joli Windows.form)
  • title : le moment concerné par la prévision. A l’heure où j’écris ces lignes – nous sommes le mercredi 24 mai 2017 et il est 9:57 – j’ai huit nœuds forecastday (sans s) dont le title est : mercredi, mercredi soir, jeudi, jeudi soir, vendredi, vendredi soir, samedi et enfin samedi soir. Autrement dit, quatre jours ayant deux prévisions chacun.
  • fcttext_metric : le texte de la prévision utilisant le système métrique (parce les °F et les mph, merci bien, ça me parle pas trop, même si j’arrive à convertir de tête à la louche en °C et km/h, c’est quand même pas pratique. Alors profitons du système métrique puisqu’il est là).
  • pop : personnellement je ne l’ai pas retenu, mais cela signifie « Probability of precipitation« , autrement dit le pourcentage de chance pour qu’il tombe quelque chose (de la pluie, de la neige fondue, de la grêle, des grenouilles…) de mesurable dans les conditions météo actuelles (s’il fait très beau, pop sera égal à 0) . Franchement, c’est juste une probabilité, la météo est loin d’être une science exacte et franchement savoir qu’il y a 40% de chance qu’il pleuve (ou pas) dans la journée ne va pas me dire si je prends un parapluie ou non. De toutes façons je n’en prends jamais, alors bon… Et puis s’il fait moche, je reste chez moi, et puis c’est tout.

Bien, voyons maintenant comment analyser tous nos nœuds forecastday (sans s). Je vous rappelle que l’intégralité du fichier XML est dans la variable / l’objet $result :

$forecastdays=$result.response.forecast.txt_forecast.forecastdays
foreach($day in $forecastdays.ChildNodes)
{
    Write-Host "Prévisions pour $($day.title) : $($day.fcttext_metric.InnerText)"
}

Ah ben c’est tout simple ! Mais pourquoi est-ce que je définis une variable $forecastdays contenant $result.response.forecast.txt_forecast.forecastdays ? Parce qu’il y a plusieurs forecastday (sans s) et qu’ils ont tous le même nom, et ça pose rapidement un problème. L’astuce c’est de préciser à Powershell que les objets $day que l’on veut sont les enfants du nœud forecastdays (avec un s) grâce à la propriété ChildNodes (nœuds enfants, quelle coïncidence !), et le tour est joué ! Résultat :

Il va faire de plus en plus chaud mais ça se gâte samedi soir… Je vais charger les batteries de mon appareil, ce sera peut-être l’occasion de photographier de jolis éclairs !

Pour les prévisions, nous avons fait le tour. Mais vous vous doutez bien que nous n’allons pas en rester là. Voyons désormais d’autres features proposées par Wunderground…

Astronomy

Ah cette reprise de Blue Öyster Cult par Metallica… fort à propos (l’originale utilise un piano, et si ça vous intéresse de l’entendre, cliquez ici). Fin de l’aparté, revenons à nos moutons ou plutôt à nos astres, les plus importants pour notre bonne vieille Terre : le Soleil et la Lune.

Commençons par modifier notre variable $features en conséquence :

$features="conditions/forecast/astronomy/lang:FR"

Nous voyons deux nouveaux nœuds dans notre fichier XML, moon_phase et sun_phase :

Oh, encore plein de données à traiter !

Commençons par le plus simple (parce qu’il contient moins de données), le Soleil :

Write-Host "Soleil"
Write-Host "Lever : $($result.response.sun_phase.sunrise.hour):$($result.response.sun_phase.sunrise.minute)"
Write-Host "Coucher : $($result.response.sun_phase.sunset.hour):$($result.response.sun_phase.sunset.minute)`r`n"

Nous n’avons que l’heure et la minute du lever et du coucher de notre étoile, mais ça suffit largement. Il faut assembler le tout dans le bon ordre et de l’afficher. La séquence « `r`n » (le ` c’est le « carret« , c’est AltGr+7 qu’il faut taper sur le clavier) à la fin permet simplement de sauter une ligne dans la console.

Pour la Lune, il y a un peu plus d’informations que le lever (moonrise) et le coucher (moonset) de notre satellite. Il y a également son âge, en nombre de jours. Il se trouve qu’une lunaison (le temps entre deux nouvelles lunes) dure 29 jours, 12 heures et 44 minutes environ (on appelle ça le rythme synodique) alors que la Lune tourne autour de la Terre en « seulement » 27 jours, 7 heures et 43 minutes (et c’est pour ça que la Lune ne se lève jamais à la même heure chaque jour), qui est son rythme sidéral. C’est le rythme synodique qui nous intéresse, puisque nous allons ainsi pouvoir en déduire les phases de la lune : nouvelle lune, premier croissant, premier quartier, lune gibbeuse croissante, pleine lune, lune gibbeuse décroissante, dernier quartier, dernier croissant, et on recommence le cycle avec une nouvelle lune. J’ai bien dit que nous allions le déduire, car ce n’est pas indiqué dans le fichier XML. Mais comme nous avons l’âge, il suffit de faire un petit tableau (array), ou une hashtable (c’est ce que j’ai choisi) qui nous donnera le nom de la phase en fonction de l’âge de la Lune :

$moonphase=@{0="Nouvelle lune";1="Premier croissant";2="Premier croissant";3="Premier croissant";4="Premier croissant";5="Premier quartier";6="Premier quartier";
7="Premier quartier";8="Gibbeuse croissante";9="Gibbeuse croissante";10="Gibbeuse croissante";11="Gibbeuse croissante";12="Gibbeuse croissante";
14="Pleine Lune";15="Gibbeuse décroissante";16="Gibbeuse décroissante";17="Gibbeuse décroissante";18="Gibbeuse décroissante";19="Gibbeuse décroissante";20="Gibbeuse décroissante"
21="Dernier quartier";22="Dernier quartier";23="Dernier quartier";24="Dernier croissant";25="Dernier croissant";26="Dernier croissant";27="Dernier croissant";28="Dernier croissant"}

Write-Host "Lune"
Write-Host "Lever : $($result.response.moon_phase.moonrise.hour):$($result.response.moon_phase.moonrise.minute)"
Write-Host "Coucher : $($result.response.moon_phase.moonset.hour):$($result.response.moon_phase.moonset.minute)"
[int]$moonage=$result.response.moon_phase.ageOfMoon # on caste en int parce que sinon ça veut pas pour avoir la moonphase
Write-Host "Age : $moonage jours ($($moonphase.$moonage))"
Write-Host "Pourcentage illumination : $($result.response.moon_phase.percentIlluminated)%`r`n"

Comme vous le voyez, je récupère l’âge dans la variable $moonage, que je force à être un entier (autrement, que je caste en int), car à l’origine il s’agit d’une chaîne, ce qui ne fonctionne pas quand j’interroge la hashtable $moonphase si $moonage n’est pas un entier… Comme tout à l’heure pour $forecastdays, je passe par une variable intermédiaire car passer par une variable à rallonge ($result.response.moon_phase.ageOfMoon) ça ne fonctionne pas.

Enfin, je termine par le pourcentage d’illumination de la Lune : en toute logique, une nouvelle lune a un pourcentage d’illumination de 0%, une pleine de 100% et entre les deux, et bien ça varie…

Notez que Wunderground fournit une icône sympa en fonction de l’âge de la lune, mais l’URL n’est pas présente dans le fichier XML. Elle est de la forme suivante :

https://icons.wunderground.com/graphics/moonpictsnew/moonX.gif

Il suffit de remplacer le X par l’âge de la Lune et hop, on a l’icône (de 50×50 pixels) correspondant à sa phase ! Sinon, vous pouvez en dessiner vous-même, hein. De toutes façons pour l’instant cela ne nous sert à rien, mais quand nous ferons notre application en Windows.form, et bien… vous devinez la suite !

Passons désormais à une autre feature proposée par Wunderground (on ne va pas toutes les faire cela dit)…

Almanac

Par le plus grand hasard, j’écris ces lignes avec Don’t Fear The Reaper en fond sonore, toujours de Blue Öyster Cult, mais ça n’a rien à voir avec la feature Almanac ! Cela dit, cette chanson est super sympa, et vous pouvez en entendre une reprise tout aussi sympathique à la fin de Fantômes contre Fantômes de Peter Jackson. Mais revenons à notre almanac (oui je sais, en français ça s’écrit « Almanach »). Rajoutons ça à la variable $features :

$features="conditions/forecast/astronomy/almanac/lang:FR"

Et voyons le résultat dans le fichier XML :

Des données, des données et encore des données à traiter…

Que voyons-nous là : tout d’abord un code d’aéroport (airport_code)… Ah c’est bien, mais quel aéroport ? A priori le plus proche (et après vérification, Le Bourget est plus proche de chez moi que Roissy, même si j’irai plus vite à Roissy en voiture qu’au Bourget… En RER, c’est encore plus long et si vous connaissez un peu le RER B, vous avez intérêt d’avoir beaucoup de marge pour ne pas louper votre vol…), mais ils auraient pu donner le nom, ça nous aurait bien arrangé… J’y reviendrais.

Ensuite, nous avons visiblement des températures, en °F et en °C, et des années de record. Qu’est-ce que c’est que ça ? Et bien après une intense réflexion de deux secondes, voilà ce que nous allons en tirer :

C’est un almanach, donc ça a un rapport avec la date du jour…

Oh attendez, Youtube a décidé de mettre une de mes chansons préférées (en matière de rock des années 70), à savoir More Than A Feeling de Boston ! Voyez, je fais votre éducation musicale en plus de votre éducation en matière de Powershell, c’est du deux-en-un sur ce blog, c’est pratique, non ?

Bref, comme je l’ai dit en légende de l’image ci-dessus, comme il s’agit d’un almanach, on a les températures maximale et minimale de notre aéroport, et même les records observés pour aujourd’hui. Mais… mais… comment je sais que le code LFPB c’est Le Bourget ? Bah, parce que je suis cultivé et que je le sais depuis belle lurette déjà, mais mon script Powershell lui est parfaitement idiot. Et pour qu’il affiche que c’est le Bourget, il a fallu lui indiquer où chercher l’information. Et pas de chance, Wunderground ne l’indiquant nulle part, il a fallu la chercher ailleurs (et ce n’était pas simple à trouver, croyez-moi), c’est-à-dire en faisant appel à une autre API, pour laquelle il faut aussi une clé d’API et donc s’inscrire (mais là aussi c’est gratuit).

Tout d’abord, une précision : LFPB est le code OACI (ou ICAO en anglais) de l’aéroport du Bourget (OACI pour Organisation de l’aviation civile internationale, une agence des Nations Unies basée à Montréal au Canada). Il existe un autre code, nommé IATA (International Air Transport Association ou Association du transport aérien international), qui n’est qu’une association de compagnies aériennes. Le siège est aussi à Montréal au Canada, et le patron actuel n’est autre que l’ancien PDG d’Air France-KLM, Alexandre de Juniac (qui lui a gardé sa chemise intacte, contrairement à son DRH de l’époque, mais je m’égare). Pourquoi je parle du code IATA ? Parce qu’il n’a rien à voir avec le code OACI ! En effet, le Bourget a le code IATA « LBG », et vous connaissez probablement celui de Roissy-Charles-de-Gaulle, CDG (c’est imprimé sur vos billets d’avion et étiquettes de bagages), dont le code OACI est LFPG… (et pendant ce temps, Youtube enchaîne Down Under et Who Can It Be Now de Men at Work dites donc… mais si, vous connaissez forcément. Il y a aussi Overkill qui est connue…).

Ok ok, je me suis encore égaré. Bref. Ne confondez pas codes OACI et IATA, ça n’a rien à voir, mais sachez qu’il existe aussi une API pour les codes IATA, et que ça m’a donné une idée pour un autre programme… Je disais qu’il fallait avoir une clé d’API pour pouvoir savoir le nom d’un aéroport à partir du code OACI : ça se passe là (cliquez sur le bouton « Register and get a FREE API key NOW« , remplissez le formulaire et surveillez votre boîte mail). Là aussi, il faudra stocker cette clé dans une variable et appeler une URL spécialement formée pour avoir l’info :

$icaoAPIkey="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Ensuite, le bout de code qui affiche les données pour la feature almanac :

$icaoURL="https://v4p4sz5ijk.execute-api.us-east-1.amazonaws.com/anbdata/airports/locations/international-list?api_key=$icaoAPIkey&format=json&airports=$($result.response.almanac.airport_code)"
$icaoresult= Invoke-WebRequest -Uri $icaoURL | ConvertFrom-Json
$today=Get-Date -Format "d MMMM"

Write-Host "`r`nAéroport : $($icaoresult.airportName) ($($result.response.almanac.airport_code))"
Write-Host "Température maximale moyenne pour un $today : $($result.response.almanac.temp_high.normal.C)°C"
Write-Host "Température minimale moyenne pour un $today : $($result.response.almanac.temp_low.normal.C)°C"
Write-Host "Record de température maximale pour un $today : $($result.response.almanac.temp_high.record.C)°C (en $($result.response.almanac.temp_high.recordyear))"
Write-Host "Record de température minimale pour un $today : $($result.response.almanac.temp_low.record.C)°C (en $($result.response.almanac.temp_low.recordyear))"

Rien de bien sorcier là encore : l’URL est toujours de la même forme (notez que c’est hébergé sur le cloud d’Amazon, AWS), qu’on fournit la clé d’API ($icaoAPIkey) et le code OACI de l’aéroport ($result.response.almanac.airport_code). Par contre, pas de chance, l’API ne fournit pas les données au format XML mais JSON (ça se prononce comme le prénom Jason, à savoir Djay-Sonne, et pas Djay-Zonne, hein)…

Berk, du JSON…

Mais bon, pas grave, Powershell peut convertir du JSON (avec la cmdlet ConvertFrom-Json) et mettre tout ça dans un objet ($icaoresult). Il suffit ensuite d’afficher la propriété issue du JSON contenant le nom de l’aéroport, ce qui nous donne $icaoresult.airportName. Dommage d’avoir à appeler une seconde API juste pour avoir un nom d’aéroport, mais c’est comme ça. A noter qu’au passage elle est assez lente et donc le script doit attendre que madame daigne répondre avant de continuer… Bref, je vais voir si je n’en trouve pas une autre plus rapide, ou j’irai harceler Wunderground pour qu’ils rajoutent le nom de l’aéroport directement dans leur API à eux.

Enfin, je fais une petite variable $today qui va contenir la date d’aujourd’hui sous cette forme : 24 mai. Ce qui convient parfaitement à ce que j’affiche ensuite, pour les températures de l’aéroport.

Voilà, c’est tout pour aujourd’hui (mais pas d’inquiétude, j’ai déjà d’autres trucs sous le manteau), portez-vous bien et tchüss !

 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.