right  Talk To Us!

UAS API - Get Us Weather Sample Application

  • Filename:

    samples/get_us_weather.py

    Description:

    This is an inbound application that prompts the caller to enter a US Zipcode and then reads the weather for that location to the caller. It demonstrates the use of external operations to acquire data for the phone application.

    The call is answered and the TTS function channel.FilePlayer.say() is used to ask the caller for a U.S. Zipcode, ending with a # or *. The function channel.DTMFDetector.get_digits is used to collect the DTMF digits, specifying the end argument which is a list of DTMF digits that indicates to the Cloud which characters to use to end DTMF collection.

    If the correct number of digits has been entered, the GetUSWeather() function is called. This sends an enquiry to a Yahoo Weather website with the Zipcode that was entered. Depending on the status code of the request, the weather data is extracted using XML. A string with the weather summary is returned by the function.

    Finally, TTS is used in the main function to say the text. Then the call is hung up.

    Code:

    """
    An inbound application that prompts the caller to enter
    a US Zipcode and then reads the weather for that location to the caller.
    
    It gets the weather forecast using the Yahoo query API.
     
    """
    
    __uas_identify__ = "application"
    __uas_version__ = "1.0b3"
    
    import sys, os
    if sys.version > '3':
        import http.client
    else:
        import httplib
    
    from prosody.uas import Hangup, Error, ICanRun
    
    from urllib import quote
    import json
    
    # expand US state abbreviation to full name
    def us_state(abbreviation):
        return {
                'AK': 'Alaska',
                'AL': 'Alabama',
                'AR': 'Arkansas',
                'AS': 'American Samoa',
                'AZ': 'Arizona',
                'CA': 'California',
                'CO': 'Colorado',
                'CT': 'Connecticut',
                'DC': 'District of Columbia',
                'DE': 'Delaware',
                'FL': 'Florida',
                'GA': 'Georgia',
                'GU': 'Guam',
                'HI': 'Hawaii',
                'IA': 'Iowa',
                'ID': 'Idaho',
                'IL': 'Illinois',
                'IN': 'Indiana',
                'KS': 'Kansas',
                'KY': 'Kentucky',
                'LA': 'Louisiana',
                'MA': 'Massachusetts',
                'MD': 'Maryland',
                'ME': 'Maine',
                'MI': 'Michigan',
                'MN': 'Minnesota',
                'MO': 'Missouri',
                'MP': 'Northern Mariana Islands',
                'MS': 'Mississippi',
                'MT': 'Montana',
                'NA': 'National',
                'NC': 'North Carolina',
                'ND': 'North Dakota',
                'NE': 'Nebraska',
                'NH': 'New Hampshire',
                'NJ': 'New Jersey',
                'NM': 'New Mexico',
                'NV': 'Nevada',
                'NY': 'New York',
                'OH': 'Ohio',
                'OK': 'Oklahoma',
                'OR': 'Oregon',
                'PA': 'Pennsylvania',
                'PR': 'Puerto Rico',
                'RI': 'Rhode Island',
                'SC': 'South Carolina',
                'SD': 'South Dakota',
                'TN': 'Tennessee',
                'TX': 'Texas',
                'UT': 'Utah',
                'VA': 'Virginia',
                'VI': 'Virgin Islands',
                'VT': 'Vermont',
                'WA': 'Washington',
                'WI': 'Wisconsin',
                'WV': 'West Virginia',
                'WY': 'Wyoming'
               }.get(abbreviation.strip(), abbreviation)
    
    
    def get_woeid(zip_code):
    
        # Get the "where on earth ID" from the zip code
        if sys.version > '3':
            http_connect = http.client.HTTPConnection("query.yahooapis.com")
        else:
            http_connect = httplib.HTTPConnection("query.yahooapis.com")
    
        query = quote('select woeid from geo.places(1) where text = "{0}"'.format(zip_code))
        request = '/v1/public/yql?q={0}&format=json'.format(query)
        http_connect.request("GET", request)
        
        response = http_connect.getresponse()
        if response.status == 200:
            data = json.loads(response.read())
            print("W: {0}".format(data["query"]["results"]))
            woeid = data["query"]["results"]["place"]["woeid"]
            return woeid
        else:
            return None
    
    def get_weather(woeid, my_log=None):
    
        if sys.version > '3':
            http_connect = http.client.HTTPConnection("query.yahooapis.com")
        else:
            http_connect = httplib.HTTPConnection("query.yahooapis.com")
    
        query = quote('select * from weather.forecast where woeid = {0}'.format(woeid))
        request = '/v1/public/yql?q={0}&format=json'.format(query)
        http_connect.request("GET", request)
        
        response = http_connect.getresponse()
        if response.status == 200:
            data = json.loads(response.read())
            results = data["query"]["results"]["channel"]
            country = results["location"]["country"]
            if country == "United States":
                region = us_state(results["location"]["region"])
            else:
                region = country
    
            location  = results["location"]["city"] + ", " + region
            forecasts = results["item"]["forecast"]
    
            high_f = float(forecasts[0]["high"])
            high_c = int((high_f - 32.0) * 5.0/9.0)
            temp = "{0} Fahrenheit, {1} Celsius".format(high_f, high_c)
            weather = "{0}. Today's weather is {1}, with a high of {2}.".format(location, forecasts[0]["text"], temp)
            
            high_f = float(forecasts[1]["high"])
            high_c = int((high_f - 32.0) * 5.0/9.0)
            temp = "{0} Fahrenheit, {1} Celsius".format(high_f, high_c)
            weather = weather + " Tomorrow will be {0}, with a high of {1}.".format(forecasts[1]["text"], temp)
    
            if my_log:
                my_log.info("{0}".format(weather))
            print("{0}".format(weather))
            return weather
        else:
            print("R: {0}".format(response.status))
            return None
    
    
    def i_say(channel, to_say):
        cause = channel.FilePlayer.say(to_say, barge_in=True)
        if cause != channel.FilePlayer.Cause.NORMAL and cause != channel.FilePlayer.Cause.BARGEIN:
            raise Error("Say failed: cause is {0}".format(cause))
    
    
    def main(channel, application_instance_id, file_man, my_log, application_parameters):
        my_log.info("Get US weather started")
        dtmf = ""
        try:
            channel.ring(2)
            channel.answer()
            i_say(channel, "Hello, please enter a five digit US zipcode, followed by the pound sign.")
            
            # use ICanRun to prevent accidental infinite loop
            i_can_run = ICanRun(channel)
            while i_can_run:
                dtmf = channel.DTMFDetector.get_digits(end='#*', seconds_predigits_timeout=30)
                cause = channel.DTMFDetector.cause()
                if cause == channel.DTMFDetector.Cause.END:
                    dtmf = dtmf[:-1]
                    # check results
                if len(dtmf) == 5:
                    break
                channel.DTMFDetector.clear_digits()
                i_say(channel, "Five digits are required, please try again.")
            my_log.info("Zip code entered is {0}".format(dtmf))
            woeid = get_woeid(dtmf)
            if not woeid:
                i_say(channel, "Sorry but I can't find a region for that zip code.")
            else:
                try:
                    weather = get_weather(woeid, my_log)
                except:
                    weather = "I cannot find the weather for that location."
                i_say(channel, weather)
            i_say(channel, "Thank you for calling, goodbye")
            channel.hang_up()
    
            my_log.info("Get weather completed")
            return 0
            
        except Hangup as exc:
            my_log.info("Get weather completed with Hangup")
            return 0
                
        except Error as exc:
            my_log.error("Get weather completed with Error exception! {0}".format(exc))
            return -101
    
        except Exception as exc:
            my_log.exception("Get weather completed with exception! {0}".format(exc))
            return -102
    
        finally:
            if channel.state() != channel.State.IDLE:
                channel.hang_up()
    
  • Filename:

    Samples\C#\GetUSWeather\GetUSWeather.cs

    Description:

    The call is answered and some TTS is played, prompting the user to enter the US Zipcode of the location for which a weather update is required. We use an overloaded version of the DtmfDetector.GetDigits method, specifying two possible end digits, instead of a maximum number of digits. We specify false for the clear parameter to allow 'type-ahead' of the digits and we set the maximum time to wait for the first digit to 10 seconds and the maximum interval between successive digits to 30 seconds. The detected DTMF values are stored in the zipCode string.

    At this stage we do some error checking against the possible causes as to why the method finished. We can also check the length of the resultant string, to ensure the correct number of digits have been entered. This is useful if you need to do some validity checking of keypad entry, or if the number of digits is not always fixed in your application.

    The GetUSWeather method contacts an external website with the Zipcode our caller has entered as part of the URL. The resultant XML data is processed by the method, extracting the yweather:condition node and extrapolating certain data, such as the date, general description of the current weather and the temperature, which is then read back to the caller using TTS.

    The application then signs off and hangs up the call.

    Code:

    using System;
    using AMSClassLibrary;
    using UASAppAPI;
    using System.Net;
    using System.IO;
    using System.Xml;
    using System.Text;
    
    // An inbound application that answers the call, prompts the caller to enter
    // a 5 digit US Zipcode then reads the weather for that location to the caller.
    // e.g.
    // 90212 - Beverley Hills CA
    // 10036 - Times Square, NY
    // 33139 - South Beach, FL
    // 94129 - San Francisco, CA
    // 20011 - Washington, DC
    // 90028 - Hollywood, CA
    namespace GetUSWeather
    {
        // The application class.
        // This must have the same name as the assembly and must inherit from either 
        // UASInboundApplication or UASOutboundApplication.
        // It must override the Run method.
        public class GetUSWeather : UASInboundApplication
        {
            // Possible return codes
            enum ReturnCode
            {
                // Success Codes:
                Success = 0,
                // ... any positive integer
    
                // Fail Codes:
                // -1 to -99 reserved
                ExceptionThrown = -100
            }
    
            string zipCode = "";
    
            // This is the entry point for the application
            public override int Run(UASCallChannel channel,
                                    string applicationParameters)
            {
                this.Trace.TraceInfo("Started");
                ReturnCode reply = ReturnCode.Success;
    
                try
                {
                    // Answer the call
                    CallState state = channel.Answer();
                    if (state == CallState.Answered)
                    {
                        // Call answered
                        this.Trace.TraceInfo("Call answered - callFrom: [{0}]",
                                        channel.CallDetails.CallFrom);
    
                        // Prompt the caller to enter a 5 digit zip code
                        FilePlayerCause playCause = channel.FilePlayer.Say(
                                        "Hello, please enter a 5 digit zipcode.",
                                        true);
                        
                        do
                        {
                            DtmfDetectorCause detCause = channel.DtmfDetector.GetDigits(
                                        5, null, out zipCode, false, 20, 10);
                            if (detCause == DtmfDetectorCause.HangUp)
                            {
                                break;
                            }
                            if (detCause == DtmfDetectorCause.Count)
                            {
                                break;
                            }
                            channel.DtmfDetector.ClearDigits();
                            channel.FilePlayer.Say("5 digits are required, please try again.", true);
                        } while (true);
    
                        if (channel.State == CallState.Answered)
                        {
                            this.Trace.TraceInfo("zipCode entered is [{0}]", zipCode);
    
                            // Get the weather forecast
                            string weatherText = GetWeather(zipCode);
                            if (weatherText == null)
                            {
                                channel.FilePlayer.Say("I could not obtain weather information for zipcode {0}. " +
                                        "Thank you for calling, goodbye.", zipCode);
                            }
                            else
                            {
                                channel.DtmfPlayer.Play("A");
    
                                channel.FilePlayer.Say(weatherText);
                                channel.FilePlayer.Say("Thank you for calling, goodbye.");
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    this.Trace.TraceError("Exception thrown {0}", e.Message);
                    reply = ReturnCode.ExceptionThrown;
                }
                finally
                {
                    channel.HangUp();
                }
    
                this.Trace.TraceInfo("Completed");
    
                return (int)reply;
            }
    
    
            // Scrape a us weather rss feed for brief details by zip code
            private string GetWeather(string zipCode)
            {
                StringBuilder weather = new StringBuilder();
    
                string query = String.Format("select * from weather.forecast where woeid in (select woeid from geo.places(1) where text=\"{0}\")", zipCode);
                string url = String.Format("https://query.yahooapis.com/v1/public/yql?q={0}&format=xml", query);
                WebRequest req = WebRequest.Create(url);
                if (req != null)
                {
                    using (StreamReader streamReader = new StreamReader(req.GetResponse().GetResponseStream()))
                    {
                        XmlDocument doc = new XmlDocument();
                        doc.Load(streamReader);
    
                        XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
                        nsMgr.AddNamespace("yweather", "http://xml.weather.yahoo.com/ns/rss/1.0");
                        XmlNode node = doc.SelectSingleNode("//yweather:location", nsMgr);
                        string location = node.Attributes.GetNamedItem("city").Value;
    
                        weather.Append("The weather for ");
                        weather.Append(location);
                        weather.Append(" is ");
    
                        node = doc.SelectSingleNode("//yweather:condition", nsMgr);
                        weather.Append(node.Attributes.GetNamedItem("temp").Value);
                        weather.Append(" degrees Fahrenheit, ");
                        weather.Append(node.Attributes.GetNamedItem("text").Value);
                        weather.Append(".");
    
                        this.Trace.TraceInfo(weather.ToString());
                    }
                }
    
                return weather.ToString();
            }
        }
    }
    
  • Filename:

    Samples\VB\GetUSWeather\GetUSWeather.vb

    Description:

    The call is answered and some TTS is played, prompting the user to enter the US Zipcode of the location for which a weather update is required. We use an overloaded version of the DtmfDetector.GetDigits method, specifying two possible end digits, instead of a maximum number of digits. We specify false for the clear parameter to allow 'type-ahead' of the digits and we set the maximum time to wait for the first digit to 10 seconds and the maximum interval between successive digits to 30 seconds. The detected DTMF values are stored in the zipCode string.

    At this stage we do some error checking against the possible causes as to why the method finished. We can also check the length of the resultant string, to ensure the correct number of digits have been entered. This is useful if you need to do some validity checking of keypad entry, or if the number of digits is not always fixed in your application.

    The GetUSWeather method contacts an external website with the Zipcode our caller has entered as part of the URL. The resultant XML data is processed by the method, extracting the yweather:condition node and extrapolating certain data, such as the date, general description of the current weather and the temperature, which is then read back to the caller using TTS.

    The application then signs off and hangs up the call.

    Code:

    Imports AMSClassLibrary
    Imports UASAppAPI
    Imports System.Net
    Imports System.IO
    Imports System.Xml
    Imports System.Text
    
    ' An inbound application that answers the call, prompts the caller to enter
    ' a 5 digit US Zipcode then reads the weather for that location to the caller.
    ' e.g.
    ' 90212 - Beverley Hills CA
    ' 10036 - Times Square, NY
    ' 33139 - South Beach, FL
    ' 94129 - San Francisco, CA
    ' 20011 - Washington, DC
    ' 90028 - Hollywood, CA
    Namespace GetUSWeather
    
        ' The application class.
        ' This must have the same name as the assembly and must inherit from either 
        ' UASInboundApplication or UASOutboundApplication.
        ' It must override the Run method.
        Public Class GetUSWeather
            Inherits UASInboundApplication
    
            ' Possible return codes
            Enum ReturnCode
                ' Success Codes:
                Success = 0
                ' ... any positive integer
    
                ' Fail Codes:
                ' -1 to -99 reserved
                ExceptionThrown = -100
            End Enum
    
            Private zipCode = ""
    
            ' This is the entry point for the application
            Overrides Function Run(ByVal channel As UASCallChannel,
                                   ByVal applicationParameters As String) _
                                   As Integer
    
                Me.Trace.TraceInfo("Started")
                Dim reply As ReturnCode = ReturnCode.Success
    
                Try
                    ' Answer the call
                    Dim state As CallState
                    state = channel.Answer()
    
                    If state = CallState.Answered Then
                        Me.Trace.TraceInfo("Call answered - callFrom: [{0}]",
                                        channel.CallDetails.CallFrom)
    
                        ' Prompt the caller to enter a 5 digit zip code
                        Dim playCause = channel.FilePlayer.Say("Hello, please enter a 5 digit zipcode.", True)
    
                        Do
                            Dim detCause = channel.DtmfDetector.GetDigits(5, Nothing, zipCode, False, 20, 10)
                            If detCause = DtmfDetectorCause.HangUp Then
                                Exit Do
                            End If
                            If detCause = DtmfDetectorCause.Count Then
                                Exit Do
                            End If
                            channel.DtmfDetector.ClearDigits()
                            channel.FilePlayer.Say("5 digits are required, please try again.", True)
                        Loop
    
                        If channel.State = CallState.Answered Then
                            Me.Trace.TraceInfo("zipCode entered is [{0}]", zipCode)
    
                            ' Get the weather forecast
                            Dim weatherText = GetWeather(zipCode)
                            If weatherText Is Nothing Then
                                channel.FilePlayer.Say("I could not obtain weather information for zipcode {0}. " +
                                        "Thank you for calling, goodbye.", zipCode)
                            Else
                                channel.DtmfPlayer.Play("A")
    
                                channel.FilePlayer.Say(weatherText)
                                channel.FilePlayer.Say("Thank you for calling, goodbye.")
                            End If
                        End If
                    End If
    
                Catch ex As Exception
                    Me.Trace.TraceError("Exception thrown {0}", ex.Message)
                    reply = ReturnCode.ExceptionThrown
                Finally
                    channel.HangUp()
                End Try
    
                Me.Trace.TraceInfo("Completed")
                Return reply
    
            End Function
    
            ' Scrape a us weather rss feed for brief details by zip code
            Private Function GetWeather(ByVal zipCode As String) As String
    
                Dim weather As StringBuilder = New StringBuilder
    
                Dim query = String.Format("select * from weather.forecast where woeid in (select woeid from geo.places(1) where text=""{0}"")", zipCode)
                Dim url = String.Format("https://query.yahooapis.com/v1/public/yql?q={0}&format=xml", query)
    
                Dim req = WebRequest.Create(url)
                If Not req Is Nothing Then
                    Using StreamReader = New StreamReader(req.GetResponse().GetResponseStream())
                        Dim doc = New XmlDocument()
                        doc.Load(StreamReader)
    
                        Dim nsMgr = New XmlNamespaceManager(doc.NameTable)
                        nsMgr.AddNamespace("yweather", "http://xml.weather.yahoo.com/ns/rss/1.0")
    
                        Dim node = doc.SelectSingleNode("//yweather:location", nsMgr)
                        Dim location = node.Attributes.GetNamedItem("city").Value
    
                        weather.Append("The weather for ")
                        weather.Append(location)
                        weather.Append(" is ")
    
                        node = doc.SelectSingleNode("//yweather:condition", nsMgr)
                        weather.Append(node.Attributes.GetNamedItem("temp").Value)
                        weather.Append(" degrees Fahrenheit, ")
                        weather.Append(node.Attributes.GetNamedItem("text").Value)
                        weather.Append(".")
    
                        Me.Trace.TraceInfo(weather.ToString())
                    End Using
                End If
    
                Return weather.ToString()
    
            End Function
        End Class
    
    End Namespace