Python Android Weather Forecast Script

28 04 2010

I am a proud owner of an HTC Hero Android phone.  I’ve recently  been messing around with one very cool looking application, that allows you to run your own scripts on the phone. This app is called Android Scripting Environment (ASE) and it is truly ace. You can use it to write and run your own Python, Shell (Bash), Lua and Ruby scripts directly on your phone. It also hooks into various Android features, allowing you to make use of various cool Android functions, one of which is getting your location and doing cool things with that information.

I’ve been wanting to get into Python for a long time and I decided that now was a good a time as any. ASE comes with a few sample scripts to get you going, one of which gets the current weather conditions for your current location and speaks it outloud (say_weather.py which calls weather.py). As cool as this is, being told the current conditions isn’t that useful, I wanted to know the forecast for the next day. I thus delved in and did lots of Googling, mainly using this awesome page that teaches you how to parse XML files (which is the format Google delivers its weather forecast info in). The biggest issue was the XML feed from Google doesn’t clarify the difference between the five day forecasts until you get to an actual data reading. The XML feed looks like this:

<xml_api_reply version="1">
      <weather module_id="0" tab_id="0" blah>
		<forecast_information>
			<city data="Bristol, Avon"/>
                        <postal_code data="bs167eb"/>
                        <latitude_e6 data=""/>
                        <longitude_e6 data=""/>
			<forecast_date data="2010-04-28"/>
                        <current_date_time data="2010-04-28 12:50:00 +0000"/>
                        <unit_system data="US"/>
		</forecast_information>
		<current_conditions>
			<condition data="Clear"/>
			<temp_f data="64"/><temp_c data="18"/>
			<humidity data="Humidity: 52%"/>
			<icon data="/ig/images/weather/sunny.gif"/>
			<wind_condition data="Wind: S at 16 mph"/>
		</current_conditions>
		<forecast_conditions>
			<day_of_week data="Wed"/>
			<low data="51"/>
			<high data="66"/>
			<icon data="/ig/images/weather/chance_of_rain.gif"/>
			<condition data="Chance of Rain"/>
		</forecast_conditions>
		<forecast_conditions>
			<day_of_week data="Thu"/>
			<low data="46"/>
			<high data="60"/>
			<icon data="/ig/images/weather/chance_of_rain.gif"/>
			<condition data="Chance of Rain"/>
		</forecast_conditions>
		<forecast_conditions>
			<day_of_week data="Fri"/>
			<low data="48"/>
			<high data="55"/>
			<icon data="/ig/images/weather/chance_of_rain.gif"/>
			<condition data="Chance of Rain"/>
		</forecast_conditions>
		<forecast_conditions>
			<day_of_week data="Sat"/>
			<low data="44"/>
			<high data="59"/>
			<icon data="/ig/images/weather/chance_of_rain.gif"/>
			<condition data="Chance of Rain"/>
		</forecast_conditions>
	</weather>

Now, I know nothing about XML at all, but I found it impossible to get the first actual forecast from the XML data, as each forecast was simply called “forecast_conditions”, using the method in the original weather.py. Thus, after reading the handy tutorial from faqs.org (listed above) I realised I needed to use the nested childNodes function of the xml.dom function that you use in Python to parse XML files.

After lots of trial and error, I worked out the following. A node is a name given to a section named via

<some_name>

. First, you assign a name (in this came dom) to the XML you are reading by doing: dom = minidom.parseString(xml_response), then I want to get information out of the node. To move through the nodes, you use the following command dom1Node = dom.firstChild where dom was the name of the original XML file. All this does is move to the first node and give it a name (dom1Node in my case), thus we are now at

<xml_api_reply version="1">

. You then use dom2Node = dom1Node.firstChild to move to the next node and give it a name, thus we are now at

<weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0">

. Finally, we have got somewhere, because the childNodes (i.e. the ones beneath the node we are at) are the ones with interesting info, such as

<forecast_information>

and most importantly

<forecast_conditions>

. So, finally, we can now assign names to these childNodes that we want. So we use current = dom2Node.childNodes[1] for the node (the numbering starts at 0 not 1) and forecast = dom2Node.childNodes[2] for the first forecast. Finally, to get the actual data out of those nodes, we use the following command data['flow'] = forecast.getElementsByTagName('low')[0].getAttribute('data'). What this does is give the name “flow” to the data element with the Tag name “low” from the “forecast” node that we defined just above. So, essentially, it navigates to and extracts the value “51” from it.

Thus, using this technique, you can get all the forecast data you want. I therefore ended up with this script, forecast.py, to replace the weather.py, to return forecast data:
And I then edited the say_weather.py to say the forecast as well as the current conditions, resulting in say_forecast.py:

The only problem I had with this was that the Google XML feed only appears to give the temp in fahrenheit, not celcius. I can see that you can set the iGoogle to display the temp in celcius, but I couldn’t see how to get that info from a feed.

Now, the next thing on the cards is to make a widget that displays this data on the homescreen. I was thinking of saving the output of the python script to a text file and then make a widget that reads that text file and displays the data. If I can do that, I can then modify the python script to do all sorts of cool things (get news, info from my mythtv HTPC etc).

Sadly, it seems on my version of Android (Cupcake (1.5)) you can’t call ASE scripts automatically via Locale or Tasker, since I get an ASE Force Closed message whenever I try to 😦 So these currently have to be run manually. Still, this is my first piece of Python coding and it was much easier than I thought it would be, once I got my head round it all.

Advertisements

Actions

Information

3 responses

1 11 2010
Ira

I tried to run this on my Droid 2 (2.2) but I get a KeyError:’latitude’

in the line addresses = droid.geocode(location[‘latitude’],location[‘longitude’])

Thanks

1 11 2010
prupert

I got a similar error message a while after first writing this script. It seems there are two possible errors. Firstly, this sometimes occurs if you have a poor wireless connection and the phone can’t get the location from the network.

Secondly, this also occurred when I used an older version of ASE on Android 1.6. This script was written on an old version of ASE on an much older version of Android than you are using, so it is more likely this is the reason it is failing.

I shall have a look when I get a chance to see if I can get it working on 2.2.

Sorry I can’t be more helpful, I got kinda annoyed with ASE on Android. It is a clever idea, but in reality it doesn’t give you that much freedom. I ended up switching to Tasker, that can do pretty much all that ASE can do.

10 11 2011
Dustin

instead of local try llama. start them as shortcuts>scripts

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s