Read news and other information on an Android phone using text to speech

This example creates a HTTP service in node red which I call from an Android phone using Tasker. The service collects information form weather service, Youtube API and twitter, formats as text and sends back to the phone where is is converted to speech.

This is just a framework to show how these components work together.

Watch this Youtube video if you need more details how this works: https://youtu.be/ael8zoDmpFM

[{"id":"a41df692.950fb8","type":"file in","z":"f10a1076.cc20b","name":"","filename":"/home/pi/forecast_io_bud.json","format":"utf8","x":347.5714416503906,"y":750,"wires":[["7a3a50b3.a5e51"]]},{"id":"9375bc23.9ff47","type":"inject","z":"f10a1076.cc20b","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":116.57144165039062,"y":750,"wires":[["a41df692.950fb8"]]},{"id":"7a3a50b3.a5e51","type":"json","z":"f10a1076.cc20b","name":"","x":513.0714416503906,"y":918,"wires":[["46be9f2.a16676"]]},{"id":"46be9f2.a16676","type":"function","z":"f10a1076.cc20b","name":"Parse Forecast.io data","func":"var forecast = \"\";\n// current weather\nforecast = forecast + \"The weather is currently \" + msg.payload.currently.summary.toLowerCase() + \" with a temperature of \" + msg.payload.currently.temperature + \" degrees.\";\n\n// forecast for today\nforecast = forecast + \" Forecast for today is \" + msg.payload.daily.data[0].summary.toLowerCase();\nforecast = forecast + \" Temperature from \" + msg.payload.daily.data[0].temperatureMin + \" to \" + msg.payload.daily.data[0].temperatureMax + \" degrees.\";\nif (msg.payload.daily.data[0].precipType!==undefined) {\n    forecast = forecast + \" Chance of \" + msg.payload.daily.data[0].precipType + \" is \" + msg.payload.daily.data[0].precipProbability + \" percent.\";\n}\n// Convert wind bearing to direction\nvar WindDirection = \"\";\nif (msg.payload.daily.data[0].windBearing>=0 && msg.payload.daily.data[0].windBearing<22.5) {\n    WindDirection = \"North\";\n}\nif (msg.payload.daily.data[0].windBearing>=22.5 && msg.payload.daily.data[0].windBearing<67.5) {\n    WindDirection = \"North-East\";\n}\nif (msg.payload.daily.data[0].windBearing>=67.5 && msg.payload.daily.data[0].windBearing<112.5) {\n    WindDirection = \"East\";\n}\nif (msg.payload.daily.data[0].windBearing>=112.5 && msg.payload.daily.data[0].windBearing<157.5) {\n    WindDirection = \"South-East\";\n}\nif (msg.payload.daily.data[0].windBearing>=157.5 && msg.payload.daily.data[0].windBearing<202.5) {\n    WindDirection = \"South\";\n}\nif (msg.payload.daily.data[0].windBearing>=202.5 && msg.payload.daily.data[0].windBearing<247.5) {\n    WindDirection = \"South-West\";\n}\nif (msg.payload.daily.data[0].windBearing>=247.5 && msg.payload.daily.data[0].windBearing<292.5) {\n    WindDirection = \"West\";\n}\nif (msg.payload.daily.data[0].windBearing>=292.5 && msg.payload.daily.data[0].windBearing<337.5) {\n    WindDirection = \"North-West\";\n}\nif (msg.payload.daily.data[0].windBearing>=337.5) {\n    WindDirection = \"North\";\n}\nforecast = forecast + \" Wind speed is \" + msg.payload.daily.data[0].windSpeed + \" kilometers per hour from \" + WindDirection + \".\";\n\n// Calculate sunset time\nvar sunset = new Date(msg.payload.daily.data[0].sunsetTime*1000);\n\nforecast = forecast + \" Sunset is at \" + sunset.getHours() + \":\" + sunset.getMinutes() + \".\";\n\nforecast = forecast + \" Tomorrow will be \" + msg.payload.daily.data[1].summary.toLowerCase();\n\nglobal.set(\"MorningStat_forecast\", forecast);\n\nmsg.payload = forecast;\nreturn msg;","outputs":1,"noerr":0,"x":733.5714416503906,"y":916,"wires":[["e4cb1d9a.24784"]]},{"id":"18ba82d4.c3606d","type":"http request","z":"f10a1076.cc20b","name":"Forecast IO","method":"GET","ret":"txt","url":"https://api.darksky.net/forecast/<api key>/47.4979,19.0402?exclude=hourly,alerts,flags&units=ca","tls":"","x":269.5714416503906,"y":917,"wires":[["7a3a50b3.a5e51"]]},{"id":"53bb2178.b5acd","type":"http request","z":"f10a1076.cc20b","name":"Youtube Channel List","method":"GET","ret":"txt","url":"https://www.googleapis.com/youtube/v3/channels?part=statistics&id=<channel id>&key=<api key>","tls":"","x":299.5714416503906,"y":994,"wires":[["3f00c28.921053e"]]},{"id":"30f3205a.ac63d","type":"inject","z":"f10a1076.cc20b","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":98.57144165039062,"y":839,"wires":[["53bb2178.b5acd","18ba82d4.c3606d"]]},{"id":"3f00c28.921053e","type":"json","z":"f10a1076.cc20b","name":"","x":507.5714416503906,"y":993,"wires":[["91996c79.b2bcb"]]},{"id":"91996c79.b2bcb","type":"function","z":"f10a1076.cc20b","name":"Parse Youtube Channel list data","func":"var youtube = \"\";\n// current weather\nyoutube = youtube + \"You have \" + msg.payload.items[0].statistics.subscriberCount + \" subscribers on Youtube and \" + msg.payload.items[0].statistics.viewCount + \" view.\";\n\nglobal.set(\"MorningStat_youtube\", youtube);\n\nmsg.payload = youtube;\nreturn msg;","outputs":1,"noerr":0,"x":745.5714416503906,"y":989,"wires":[["e4cb1d9a.24784"]]},{"id":"e4cb1d9a.24784","type":"join","z":"f10a1076.cc20b","name":"","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","timeout":"10","count":"2","x":991.0714416503906,"y":935,"wires":[["22d0c046.c99c9"]]},{"id":"e8bcc953.639478","type":"http in","z":"f10a1076.cc20b","name":"","url":"/morningstat","method":"get","swaggerDoc":"","x":127.57144165039062,"y":677,"wires":[["9830b162.9599b"]]},{"id":"9830b162.9599b","type":"function","z":"f10a1076.cc20b","name":"Check if need executing","func":"var current = new Date();\nvar today = \"\";\ntoday = today.concat(current.getFullYear()).concat(current.getMonth()+1).concat(current.getDate());\n\n// check if this after 6 am\nif (current.getHours()>=6) {\n    if (today===global.get(\"MorningStat\")) {\n        msg.execute = false;\n    } else {\n        msg.execute = true;\n        global.set(\"MorningStat\",today);\n    }\n} else {\n    msg.execute = false;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":405.0714416503906,"y":678,"wires":[["308f83fb.96499c"]]},{"id":"cf907237.69f8a","type":"http response","z":"f10a1076.cc20b","name":"Response","x":992.5714111328125,"y":1132,"wires":[]},{"id":"308f83fb.96499c","type":"switch","z":"f10a1076.cc20b","name":"","property":"execute","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","outputs":2,"x":620.5714416503906,"y":680,"wires":[["18ba82d4.c3606d","53bb2178.b5acd"],["5158c81b.2fc848"]]},{"id":"5158c81b.2fc848","type":"function","z":"f10a1076.cc20b","name":"Empty response","func":"msg.payload=\"NO\";\nreturn msg;","outputs":1,"noerr":0,"x":817.5714416503906,"y":674,"wires":[["cf907237.69f8a"]]},{"id":"22d0c046.c99c9","type":"function","z":"f10a1076.cc20b","name":"Construnct full stat","func":"msg.payload = global.get(\"MorningStat_forecast\") + \"\\n\" + global.get(\"MorningStat_youtube\") + \"\\n\" + global.get(\"MorningStat_tweettext\");\nreturn msg;","outputs":1,"noerr":0,"x":731.5714416503906,"y":1133,"wires":[["cf907237.69f8a","61b1311c.5c0a"]]},{"id":"61b1311c.5c0a","type":"debug","z":"f10a1076.cc20b","name":"","active":true,"console":"false","complete":"false","x":972.5714416503906,"y":1196,"wires":[]},{"id":"f6fead90.c3d35","type":"inject","z":"f10a1076.cc20b","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":132.57144165039062,"y":1291,"wires":[["4ee8bd89.e14b74"]]},{"id":"4ee8bd89.e14b74","type":"function","z":"f10a1076.cc20b","name":"Reset trigger","func":"global.set(\"MorningStat\",\"AA\");","outputs":1,"noerr":0,"x":337.5714416503906,"y":1292,"wires":[[]]},{"id":"cf2fe681.02d378","type":"comment","z":"f10a1076.cc20b","name":"Morning Statistics","info":"","x":121,"y":615,"wires":[]},{"id":"a2719b55.710888","type":"twitter in","z":"f10a1076.cc20b","twitter":"","tags":"@BBCBreaking","user":"user","name":"","topic":"tweets","inputs":0,"x":183,"y":1186,"wires":[["9a0d5b82.c2fb18","ed3ad4e2.f47888"]]},{"id":"9a0d5b82.c2fb18","type":"debug","z":"f10a1076.cc20b","name":"","active":true,"console":"false","complete":"true","x":409.5,"y":1090,"wires":[]},{"id":"ed3ad4e2.f47888","type":"function","z":"f10a1076.cc20b","name":"Save tweets","func":"var tweet = {created: 0, text:\"\"};\nvar tweets = [];\nvar current = new Date();\nvar age = 8*60*60*1000; // 8 hours in miliseconds\n\n// load tweets if found in global\nif (global.get(\"MorningStat_tweets\")!==undefined) {\n    tweets = global.get(\"MorningStat_tweets\");\n    \n}\n\n// cycle through the tweets and delete old ones\nfor (i = 0;i<tweets.length;i++) {\n    // Check and remove tweets older than age\n    if (current-tweets[i].created>age) {\n        tweets.remove(i);\n    }\n}\n\n// Add the current tweet to the collection\ntweet.created = current;\ntweet.text = msg.tweet.text;\ntweets.push(tweet);\n\n// Save the tweet list\nglobal.set(\"MorningStat_tweets\",tweets);\n\n// Save the text of the tweets\nvar tweettext = \"Breaking news from BBC: \";\nfor (i = 0;i<tweets.length;i++) {\n    tweettext = tweettext + tweets[i].text.substring(0,tweets[i].text.indexOf(\"http\")-1) + \"\\n\";\n}\nglobal.set(\"MorningStat_tweettext\",tweettext);\n\nreturn msg;","outputs":1,"noerr":0,"x":419,"y":1190,"wires":[[]]},{"id":"b6246d7.193409","type":"debug","z":"f10a1076.cc20b","name":"","active":true,"console":"false","complete":"payload","x":633,"y":1351,"wires":[]},{"id":"ae1caf20.023d3","type":"function","z":"f10a1076.cc20b","name":"Dump Saved Tweets","func":"msg.payload = global.get(\"MorningStat_tweets\");\nreturn msg;\n","outputs":1,"noerr":0,"x":366,"y":1351,"wires":[["b6246d7.193409"]]},{"id":"e4b9ae81.b8fdb","type":"inject","z":"f10a1076.cc20b","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":109,"y":1350,"wires":[["ae1caf20.023d3"]]}]
nygma2004

Flow Info

created 6 months ago

Node Types

Core
  • comment (x1)
  • debug (x3)
  • file in (x1)
  • function (x8)
  • http in (x1)
  • http request (x2)
  • http response (x1)
  • inject (x4)
  • json (x2)
  • switch (x1)
  • twitter in (x1)
Other

Tags

  • text
  • speech
  • android
  • tasker
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option