Using Google Weather API in a VoiceXML Application

In this post I will show you how to use the Google Weather API to retrieve current weather conditions that can be voiced back to callers.  In a previous post I showed how to use VoiceModel to create a simple VoiceXML application that asks the caller for their zip code, looks up the the weather conditions for that zip, and then voices the information back to the caller; but I used a mock-up for the service that retrieves the weather information for easier testing.  This time I will demonstrate how to swap out the mock-up for a functioning service to get the current weather.  Since the underlying architecture for VoiceModel is ASP.NET MVC this method would work just as well in a web application.

The way I implemented the Weather Application the service to retrieve the weather information implements the interface IWeatherService. This allows me to easily insert a mock-up for testing or any version of a service that I wish to.  I chose the Google Weather API because it is free, easy to use, and does not require setting up a special account.  The downside to this approach is that it is not supported by Google and therefore is not well documented.  Google uses this API for it's own internal applications but for some reason has not released it to the public.  You can search the web and get a lot of information on how others are using it, but since it is not public you do risk the chance that it could change without notice.  But after seeing how I implemented this you could implement another weather service API if you so choose.

Here is the code change in WeatherCallFlowBuilder.cs that implements my new service in the class GoogleWeatherService.  I commented out the line that implemented the mock-up for comparison and easily changing back to the mock-up.  The proper way to implement this would be to use a dependency injector tool like Ninject but that is for another post.

public static CallFlow Recreate()
{
   CallFlow flow = new CallFlow();
   flow.AddStartState(new State("greeting", "getZip"));
   flow.AddState(new State("getZip", "getWeather"));
   //Uncomment next line and comment the following if you want to use the mockup 
   //instead of the Google service
   //flow.AddState(new GetWeather("getWeather", "voiceWeather", new DAL.WeatherServiceMockup()));
   //This implementation uses the Google Weather API to get the current weather conditions
   flow.AddState(new GetWeather("getWeather", "voiceWeather", new DAL.GoogleWeatherService()));
   flow.AddState(new State("voiceWeather", "goodbye"));
   flow.AddState(new State("goodbye"));
   return flow;
}


The IWeatherService interface has one method that needs to be implemented and here is the implementation for GoogleWeatherService.

public Weather getWeather(string zipcode)
{
    string weatherSerializedXml = getResponseFromGoogle(zipcode);
    Weather w = parseXml(weatherSerializedXml);
    return w;
}

The Google Weather API returns an XML document.  Here is how I retrieve that document as a string.

private string getResponseFromGoogle(string zipcode)
{
    StringBuilder sb = new StringBuilder();
    byte[] buf = new byte[8192];

    HttpWebRequest request = (HttpWebRequest)
        WebRequest.Create("http://www.google.com/ig/api?weather=" + zipcode);

    HttpWebResponse response = (HttpWebResponse)
        request.GetResponse();

    Stream resStream = response.GetResponseStream();
    string tempString = null;
    int count = 0;

    do
    {
        count = resStream.Read(buf, 0, buf.Length);
        if (count != 0)
        {
            tempString = Encoding.ASCII.GetString(buf, 0, count);
            sb.Append(tempString);
        }
    }
    while (count > 0);

    return sb.ToString();
}

Once we have the XML document we need to parse it out for the relevant information for our voice application.

private Weather parseXml(string xmlWeather)
{
    Weather w = new Weather();
    XmlDocument weatherDoc = new XmlDocument();
    weatherDoc.LoadXml(xmlWeather);
    XmlNode wNode = weatherDoc.SelectSingleNode("/xml_api_reply/weather/current_conditions/condition");
    w.conditions = wNode.Attributes["data"].Value;
    wNode = weatherDoc.SelectSingleNode("/xml_api_reply/weather/current_conditions/temp_f");
    w.temp = wNode.Attributes["data"].Value;
    wNode = weatherDoc.SelectSingleNode("/xml_api_reply/weather/current_conditions/wind_condition");
    string windCondition = wNode.Attributes["data"].Value;
    char[] delims = { ' ' };
    string[] windParts = windCondition.Split(delims);
    w.windDirection = windParts[1];
    w.windSpeed = windParts[3];
    return w;
}

You can get the full source code for this example on CodePlex.  If you do not have an IVR to test this application on you can test this example using Voxeo's free IVR.  Look at my previous posts on how to test with Voxeo's IVR as either a hosted service or installed on your workstation.

Popular posts from this blog

Using Claims in ASP.NET Identity

Adding Email Confirmation to ASP.NET Identity in MVC 5

Customizing ASP.NET Identity in MVC 5