La météo vocale avec du NodeJS avec un peu de MP3 et du ftp janvier 31, 2018 Dans cet article je vais essayer de vous faire partager un petit outil qui est en place sur ce site : une annonce vocale de la météo. Mais d’ou vient cette idée ? Assez simple en parcourant mon compte twitter et le fil des actualités , j’avais trouvé un tweet de @pirmax faisant référence a un article sur le site Yubigeek. Un article proposait de créer un « bot » (un robot) qui dicterait la météo vocalement. La mise en oeuvre : Je ne vais pas reprendre tout l’article de Maxence mais en gros , il vous faut coté matériel : Un raspberry Pi un petit haut parleur (ou des écouteurs « pour tester ») Coté système : Une distribution Raspberry PI NodeJS Quelques dépendances de NodeJS pour faire fonctionner ce bot Et donc si tout se passe bien , vous aurez une belle voix qui vous annoncera la météo tous les X temps (en fonction de votre cron intégré au script, j’y reviens plus bas). Jusque la , tout fonctionne … oui mais bon entendre une voix robotisée « réciter » sa météo sur haut parleur ca devient lassant. Alors que pouvons-faire de plus ? En cherchant un peu, et si vous connaissez un peu le service de traduction de Google , il est tout à fait possible de télécharger une phrase dictée au format MP3 Je voulais donc trouver un système qui permettrait non plus de dicter la phrase mais la télécharger au format MP3. J’ai commencé à chercher du coté de NodeJS et je suis tombé sur ceci : https://github.com/zlargon/google-tts Et voici un exemple ici : https://github.com/zlargon/google-tts/blob/master/example/download.js Le principe : Ca initialise diverses librairies et on utilise « Google-TTS » (Google Text to speech) et on télécharge la phrase passée à la fonction : // start googleTTS('hello') .then(function (url) { console.log(url); // https://translate.google.com/translate_tts?... var dest = path.resolve(__dirname, 'hello.mp3'); // file destination console.log('Download to ' + dest + ' ...'); return downloadFile(url, dest); }) .then(function () { console.log('Download success'); }) .catch(function (err) { console.error(err.stack); }); Mais il fallait « merger » les 2 scripts : le premier qui permettait de récupérer une phrase et le deuxième qui lui s’occupe du téléchargement pour le stocker en local sur le raspberry et egalement l’option suplémentaire uploader le fichier mp3 sur ce site. J’ai donc utilisé un ftp simple en nodeJS dans la boucle du scheduler Voici le script complet : var request = require('request'); var player = require('play-sound')(opts = {player: "omxplayer"}); var googleTTS = require('google-tts-api'); var schedule = require('node-schedule'); var fs = require('fs'); var path = require('path'); var http = require('http'); var https = require('https'); var urlParse = require('url').parse; //------------------------------------------------------------------------------------------------------ // client ftp pour uploads var FTP = require('ftp-simple'), config = { host: 'adresse_de_votre_ftp', port: 21, user: 'utilisateur', password: 'mot_de_passe' }, ftp = FTP.create(config); //-------------------------------------------------------------------------------------------------------- let downloadFile = (url, dest) => { return new Promise(function (resolve, reject) { var info = urlParse(url); var httpClient = info.protocol === 'https:' ? https : http; var options = { host: info.host, path: info.path, headers: { 'user-agent': 'WHAT_EVER' } }; httpClient.get(options, function(res) { // check status code if (res.statusCode !== 200) { reject(new Error('request to ' + url + ' failed, status code = ' + res.statusCode + ' (' + res.statusMessage + ')')); return; } var file = fs.createWriteStream(dest); file.on('finish', function() { // close() is async, call resolve after close completes. file.close(resolve); }); file.on('error', function (err) { // Delete the file async. (But we don't check the result) fs.unlink(dest); reject(err); }); res.pipe(file); }) .on('error', function(err) { reject(err); }) .end(); }); } let getMp3 = (ville, pathMp3) => { request({ method: 'GET', url: 'http://www.prevision-meteo.ch/services/json/' + ville, headers: { 'cache-control': 'no-cache' } }, function (error, response, body) { var b = JSON.parse(body); var name = b.city_info.name; var sunrise = b.city_info.sunrise; var sunset = b.city_info.sunset; var day_long = b.fcst_day_0.day_long; var tmin = b.fcst_day_0.tmin; var tmax = b.fcst_day_0.tmax; // var condition = b.fcst_day_0.condition; var wnd_spd = b.current_condition.wnd_spd; var condition = b.current_condition.condition; var hour = b.current_condition.hour; //ça parle /*speak("Bonjour, nous sommes " + day_long + ".", function () { speak("Aujourd'hui, à " + name + ", le temps sera " + condition + " avec une température minimum de " + tmin + "° et une température maximum de " + tmax + "°.", function () { speak("Le soleil se lèvera à " + sunrise + " et se couchera à " + sunset + ".", function () { speak("Bonne journée à tous !", function () { console.log('La météo a été récitée !'); }); }); }); });*/ //ça DL googleTTS(`Bonjour,aujourd'hui ${day_long}, à ${name}, a ${hour} le temps est ${condition} avec une température mini de ${tmin}° et une température maxi de ${tmax }°, le vent souffle actuellement a ${wnd_spd} kilometre/heure.`, 'fr') .then(function (url) { console.log(url); // https://translate.google.com/translate_tts?... console.log('Download to ' + pathMp3 + ' ...'); return downloadFile(url, pathMp3); }) .then(function () { console.log('Download success'); }) .catch(function (err) { console.error(err.stack); }); }); } let ville = 'pont-Scorff'; let destMp3 = path.resolve(__dirname , 'meteo-du-jour.mp3'); console.log(destMp3); //getMp3( ville,destMp3 ) schedule.scheduleJob('00 01 * * * *', () => { //schedule.scheduleJob('*/1 * * * *', () => { getMp3(ville,destMp3) ftp.upload("/home/pi/meteo-mp3/meteo-du-jour.mp3", "/le_chemin_sur_votre_ftp/meteo-du-jour.mp3", function(err){}); }); Bon ça fonctionne mais après ? Mon raspberry est posé dans un coin de mon bureau , connecté au réseau local chez moi et il vit sa vie de « petit » serveur linux (en fait il redemarre en fonction des coupures de courant). Pour rappel , j’avais opté pour un raspberry il y a déjà quelques années afin de m’affranchir de la lourdeur du logiciel Windows « Graph Weather » (excellent logiciel) , et surtout de laisser mon ordinateur en fonctionnement 24/24. C’est au moment de l’achat de ma station WMR200 de chez OREGON que j’avais sauté le pas, (mes appareils de mesure sont connectés à ma base en radio et ma base en USB sur mon raspberry). Désormais j’utilise le logiciel weewx disponible en Open Source ici aussi : http://www.weewx.com/ Concernant mon script de météo vocale : il suffit que je l’exécute en ligne de commande et le tour est joué sauf que ça ne fonctionne pas de façon autonome et automatique. # nodejs meteo il a fallu que je trouve un système qui me permette de m’affranchir également de ma session putty ssh sur mon raspberry. J’ai donc installé le package pm2 : package NodeJS qui permet d’exécuter un programme NodeJs en mode « service » https://www.npmjs.com/package/pm2 il suffit de l’installer (la méthode est relativement simple et bien expliqué sur le site) et donc ensuite exécuter le programme en mode service #pm2 start meteo #pm2 stop meteo #pm2 status meteo #pm2 show meteo Voici un exemple de quelques commandes de pm2 : Les détails : Dans le script j’utilise un scheduler (un cron) : ceci me permet d’executer le script à intervalle régulier. Pour vos tests passer le scheduler à « toutes les minutes » et puis en production faites votre choix , pour ma part je l’ai passé à toutes les heures. //Toutes les minutes : schedule.scheduleJob('*/1 * * * *', () => { .... //Toutes les heures : schedule.scheduleJob('00 01 * * * *', () => { Les explications pour le scheduler : https://www.npmjs.com/package/node-schedule Autre chose pour le MP3, j’ai rajouté également un paramètre dans la fonction de google-tts à la fin , on rajoute l’argument fr sinon votre MP3 sera légèrement anglophone… googleTTS(`Bonjour,aujourd'hui ${day_long}, à ${name}, a ${hour} le temps est ${condition} avec une température mini de ${tmin}° et une température maxi de ${tmax }°, le vent souffle actuellement a ${wnd_spd} kilometre/heure.`, 'fr') Une autre chose importante : ne pas dépasser 200 caractères pour votre phrase , c’est la limite imposée par Google … Le package pour le ftp : https://www.npmjs.com/package/ftp-simple Mise en place du lecteur Audio : J’ai utilisé un simple code HTML5 : Votre navigateur n’est pas compatible Voila c’est en place , encore un truc qui ne sert a rien mais indispensable ! N’hésitez pas à partager ! Un grand Merci a Dim , le roi du NodeJS !!! (si un jour il passe par là, car il est l’auteur de 90% du code) Mise a jour le 7 decembre 2018 : suite a une maj de Clef sur Google Translate , le script ne fonctionnait plus Infos trouvées ici : https://github.com/noelportugal/google-home-notifier/issues/46 Modifer le fichier package.json pour upgrader la version de google-tts root@raspberrypi:/home/pi/meteo-mp3# more package.json { "name": "meteo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node meteo.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "google-tts-api": "0.0.4", "node-schedule": "^1.3.0", "play-sound": "^1.1.2", "request": "^2.83.0" } } Faire un #npm install google-tts-api --save A bientôt , et pensée pour Dim Navigation de l’article 19 Novembre 2016 : premier Gros coup de vent en BretagneNouveau site , nouveau design !