Talking Linux RSS Alarm Clock

I don’t like to get up early. Therefore, I stay in bed as late as possible and, as a result, never really set aside any time to check the news or even the weather before I head out the door. Although it would only take me an extra 3 minutes or so to read though stories or lookup the weather, the thought of turning on my monitor, entering my password, and going to the appropriate sites isn’t terribly attractive for me at 5:30 in the morning.

Combine the above with a general distaste for the jarring sound that my alarm alarmclock
makes got me thinking that there had to be a better way to get my morning started. I searched a bit to find MP3 alarm clocks (of which there are many) and initially used KAlarm which served the purpose of waking me up with music quite adequately.

However, I work in the financial industry, where overnight news is advantageous to hear before getting to work. The markets might be up or down by large amounts (especially these days) based on what happened overnight, so at least figuring out what is going on before getting into the office is pretty helpful. This, combined with a day where I got stuck in the rain because I hadn’t checked the weather, got me thinking — how about an alarm clock that read news and weather from an RSS feed in addition to waking me up with a song.

Here’s how I created it:

Basics:
- Python with urllib, ElementTree, and re installed
- cron
- mplayer (available on portage and apt)
- [optional] A playlist of MP3s or other songs that mplayer can play
- festival — a text-to-speech program (available on portage for gentoo and probably on apt too)

Steps

1) Install the necessary software. Emerge or apt-get mplayer and festival. Install ElementTree for python by following the instructions here. The other packages, urllib and re were already installed on my python, but if thats not the case for you, a bit of Googling can help out here.

2) Pick one song at random from your playlist. The way I chose to do this was to use a quick bash line to pick a a random line from the playlist file (playlist.m3u), and then create a new playlist with only the one song (single_song.m3u). Another thought is to just start mplayer and play a random song directly from the big playlist, but this means that the music will keep playing until you kill mplayer. I wanted to have one song play and then have my news and weather start to be read, so I play only the one song from the small playlist.

#alarm.sh -- pulls together elements of alarm clock
#Written by ajlisy, 2008
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

#Create file with single song
nlines=`cat playlist.m3u | wc -l`
random_line=`expr $RANDOM % $nlines + 1`
sed -n $random_line,${random_line}p playlist.m3u > single_song.m3u

#Play that song
mplayer -playlist single_song.m3u

#  <...>

3) Set up the basic scripts for fetching and parsing the weather from the Yahoo! RSS feeds. The idea is that 2 simple python scripts, get _weather.py and get_news.py are called from the main bash script to populate the “script” files that are read by festival.

#get_news.py -- fetches headlines from RSS feed
#Written by ajlisy 2008
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

import urllib
import re
from elementtree.ElementTree import parse

def get_headlines(news_source,num_headlines):
  news_rss_feed=parse(urllib.urlopen(news_source)).getroot()
  raw_headlines=news_rss_feed.findall('channel/item')
  headlines=[]
  for i in range(0,num_headlines):
     try:
       headlines.append(raw_headlines[i].findtext('title').splitlines()[0])
     except:
       pass
  return headlines

print "Today's top news stories      "
YAHOO_NEWS = 'http://rss.news.yahoo.com/rss/business'
NYT_NEWS='http://www.nytimes.com/services/xml/rss/nyt/Business.xml'

print "Worldwide news from New York Times"
for element in get_headlines(NYT_NEWS,5):
  try:
    print element
  except:
    pass
print "Business news from Yahoo"
for element in get_headlines(YAHOO_NEWS,5):
  try:
    print element
  except:
    pass
#get_weather.py -- adapted from the tutorial on the Yahoo
#  developers page found at
#  http://developer.yahoo.com/python/python-xml.html

import urllib
from elementtree.ElementTree import parse

WEATHER_URL = 'http://xml.weather.yahoo.com/forecastrss?p=%s'
WEATHER_NS = 'http://xml.weather.yahoo.com/ns/rss/1.0'

def weather_for_zip(zip_code):
  url = WEATHER_URL % zip_code
  rss = parse(urllib.urlopen(url)).getroot()
  forecasts = []
  for element in rss.findall('channel/item/{%s}forecast' % WEATHER_NS):
    forecasts.append({
        'date': element.get('date'),
        'low': element.get('low'),
        'high': element.get('high'),
        'condition': element.get('text')
       })
    ycondition = rss.find('channel/item/{%s}condition' % WEATHER_NS)
    return {
        'current_condition': ycondition.get('text'),
        'current_temp': ycondition.get('temp'),
        'forecasts': forecasts,
        'title': rss.findtext('channel/title')
    }

weather=weather_for_zip(10038)
print "The current weather conditions are "+weather['current_condition']+" with a temperature of "+ weather['current_temp'] + " degrees."
print "Today's forecast is " + weather['forecasts'][0]['condition'] + " with a high of " + weather['forecasts'][0]['high'] + " degrees and a low of "+weather['forecasts'][0]['low'] + " degrees"

4) Link it all together with the alarm.sh script that calls the individual “subroutines” mentioned above and assembles them into the alarm.

#alarm.sh

#Create file with single song
nlines=`cat playlist.m3u | wc -l`
random_line=`expr $RANDOM % $nlines + 1`
sed -n $random_line,${random_line}p playlist.m3u > single_song.m3u

#Play that song
mplayer -playlist single_song.m3u

#Create file to read
python get_weather.py > weather.txt
python get_news.py > news.txt

festival --tts weather.txt
festival --tts news.txt

Add the resulting script to your crontab for whenever you want to get up, and enjoy a better start to the day.

Share/Save/Bookmark

5 comments ↓

#1 Sean on 12.14.08 at 5:58 pm

Any ideas on how to get this kind of thing working for OS X? I have python and cron already, but the festival thing might be a little more tricky. Is there a way to set up the speech thing with Automator or another Mac speech thing?

Can’t wait to get cracking…

-S

#2 Trevor on 12.15.08 at 9:35 am

@Sean: I haven’t tried this yet, but OSX has a ’say’ command in Terminal that works GREAT. To try it, just open Terminal and type: say “Hello World”

I’m sure you can set it up to read from a file, just like festival.

#3 rich on 01.28.09 at 6:17 am

What version of Python are you using? I am getting this error when running….

SyntaxError: invalid token
Traceback (most recent call last):
File “get_news.py”, line 10, in
from elementtree.ElementTree import parse
ImportError: No module named elementtree.ElementTree

#4 AJ on 01.29.09 at 4:01 pm

Rich,

It looks like you dont have ElementTree installed. There’s a link above — http://effbot.org/zone/element-index.htm#installation — this is some instructions on installing it. Its been a while for me, but I think its basically just putting some files in the correct location and running a few install commands.

If you run into any trouble, just Google for “installing python modules” or something like that.

Good luck!
AJ

#5 10 Things a Power User Will Love about Linux | Random Musings on 02.01.09 at 11:00 pm

[...] read to me as I got ready. With Linux, setting this up was pretty basic. See my other blog post Talking Linux RSS Alarm Clock if you’re curious how I strung together several off-the-shelf components to create this app [...]

Leave a Comment