Download

require 'net/http'
require 'rexml/document'

BEGIN {

  $server = 'weather.yahooapis.com'

  $query  = '/forecastrss?p='

  $welcome = 'Welcome to the weather demonstration application.'

  $prompt = 'Please tell me the ZIP code for the town whose weather forecast you want to hear.'

  $regrets = 'We are sorry but we had trouble determining the weather for your ZIP code. ' +
             'If the ZIP code is valid you can try again later.'

  $ZipGrammarId  = 22
  $QuitGrammarId = 67

  $states = {
    'AL' => 'Alabama' , 
    'AK' => 'Alaska' ,
    'AZ' => 'Arizona' ,
    'AR' => 'Arkansas' , 
    'CA' => 'California' ,
    'CO' => 'Colorado' ,
    'CT' => 'Connecticut' ,
    'DE' => 'Delaware' ,
    'DC' => 'D C' ,
    'FL' => 'Florida' ,
    'GA' => 'Georgia' ,
    'HI' => 'Hawaii' ,
    'ID' => 'Idaho' ,
    'IL' => 'Illinois' ,
    'IN' => 'Indiana' ,
    'IA' => 'Iowa' ,
    'KS' => 'Kansas' ,
    'KY' => 'Kentucky' ,
    'LA' => 'Louisiana' ,
    'ME' => 'Maine' ,
    'MD' => 'Maryland' ,
    'MA' => 'Massachusetts' ,
    'MI' => 'Michigan' ,
    'MN' => 'Minnesota' ,
    'MS' => 'Mississippi' ,
    'MO' => 'Missouri' , 
    'MT' => 'Montana' ,
    'NE' => 'Nebraska' ,
    'NV' => 'Nevada' ,
    'NH' => 'New Hampshire' ,
    'NJ' => 'New Jersey' ,
    'NM' => 'New Mexico' ,
    'NY' => 'New York' ,
    'NC' => 'North Carolina' ,
    'ND' => 'North Dakota' ,
    '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' ,
    'VT' => 'Vermont' ,
    'VA' => 'Virginia' ,
    'WA' => 'Washington' ,
    'WV' => 'West Virginia' ,
    'WI' => 'Wisconsin' ,
    'WY' => 'Wyoming' 
  }

  $propertyNames = %w{ Digit1 Digit2 Digit3 Digit4 Digit5 }

}

def Answer(kall)

   # Get an instance of the recognizer, into it
   # Load a grammar that handles the request for ZIP code
   # Load another grammar that handles the request to exit

   rec = kall.getRecognizer()
   rec.loadGrammar('ZIP',  $ZipGrammarId, true, false)
   rec.loadGrammar('Quit', $QuitGrammarId, true, false)

   # Get an instance of the synthesizer and greet the caller

   syn = kall.getSynthesizer()
   syn.speak('Welcome to the weather demonstration application.')
   syn.wait(-1)

   # Repeat indefinitely

   while true

      # Start listening first and ...
      # ... then prompt for a ZIP code

      rec.listen()
      syn.speak($prompt)
     
      # Wait for the recognizer to tell us how it did
 
      conf = rec.wait(10000, 30000)
 
      # Stop listening

      rec.stopListening()

      # Quit if the caller disconnects

      break if conf < 0

      # Dump the state of the recognition for debugging

      rec.dump()
 
      # Arbitrariy we set a 65% confidence threshold

      if conf < 6500
        NoRecognition(syn, rec)
        next
      end

      # If we recognized a phrase in the Quit grammar then we are done

      break if rec.getGrammarId() == $QuitGrammarId 

      # Build a string from the key strokes

      zip = ''
      for i in 0..4
       zip += rec.getPropertyTextByName( $propertyNames[i] )
      end 

      # Apologize and move on if we heard less than 5 digits

      if zip.size() != 5
       NoRecognition(syn, rec)
       next
      end

      # Tell the caller to wait

      syn.speak('Please wait ...')

      # Amuse him with "music on hold"

      kall.loopMessage('strumming')

      # Get his forecast
    
      forecast = DoRequest(zip)

      # Stop playing and put a delay between the music and the speech

      kall.stopPlaying()
      kall.wait(500)

      # Speak the forecast

      syn.speak(forecast)
      puts forecast

      # Log the input and output to the call detail records table

      kall.cdrStatusMessage(0, zip)
      kall.cdrStatusMessage(1, forecast)

      # Let him hear the forecast before we ask if he wants to go again

      syn.wait(-1)
 
   end

   # Ta-ta for now

   syn.speak('Good bye.')
   syn.wait(-1)

end
#-------------------------------------------------------------------------------------- 
# When we don't hear 5 digits we reset the recognizer and apologize
#-------------------------------------------------------------------------------------- 
def NoRecognition(syn, rec)

  rec.reset()
  syn.speak( 'I\'m sorry, but I didn\'t get that.')
  syn.wait(-1)

end

#-------------------------------------------------------------------------------------- 
# Request the weather from the web service
#-------------------------------------------------------------------------------------- 
def DoRequest(zip)

  conx         = Net::HTTP.new($server, 80)
  resp, data = conx.get($query + zip, nil)

  if resp.code != '200'
   forecast = $regrets
   puts 'The web service fails us: ' + resp.message + ' (' + resp.code + ')'
  else  
   forecast = ParseXML(data)
  end

  return forecast

end

#-------------------------------------------------------------------------------------- 
# Parse the XML document returned
#-------------------------------------------------------------------------------------- 
def ParseXML(xml)
 
  begin

    doc  = REXML::Document.new(xml)

    city    = GetCity(doc)
    wind    = GetWind(doc)
    temp    = GetTemperature(doc)
    outlook = GetOutlook(doc)

    forecast = 'In ' + city + ' the weather is ' + outlook + 
               ' with a temperature of ' + temp + 
               '. The winds are ' + wind + '.'

  rescue Exception => e

    forecast = $regrets

    puts "\n"
    puts e.message()
    puts e.backtrace.join("\n")
    puts "\n"

  end

  return forecast
 
end

# -----------------------------------------------------------------------------------------------
# Extract the city and state from the XML document
# -----------------------------------------------------------------------------------------------
def GetCity(doc)

  temp1 = doc.root.elements["channel/yweather:location"].attributes["city"]
  temp2 = doc.root.elements["channel/yweather:location"].attributes["region"]

  if $states[temp2]
   city = temp1 + ' ' + $states[temp2] 
  else 
   city = temp1 + ' ' + temp2
  end

 return city

end

# -----------------------------------------------------------------------------------------------
# Extract the forecast from the XML document
# -----------------------------------------------------------------------------------------------
def GetWind(doc)

  temp1 = doc.root.elements["channel/yweather:wind"].attributes["direction"]
  temp2 = doc.root.elements["channel/yweather:wind"].attributes["speed"]

  wind = 'from the ' + Direction( temp1.to_i() ) + ' at ' + temp2 + ' miles per hour'

  return wind

end

# -----------------------------------------------------------------------------------------------
# Extract the temperature from the XML document
# -----------------------------------------------------------------------------------------------
def GetTemperature(doc)

  temp = doc.root.elements["channel/item/yweather:condition"].attributes["temp"]
  temp = temp + ' degrees Fahrenheit'

  return temp

end 

# -----------------------------------------------------------------------------------------------
# Extract the forecast from the XML document
# -----------------------------------------------------------------------------------------------
def GetOutlook(doc)

  outlook = doc.root.elements["channel/item/yweather:condition"].attributes["text"]

  return outlook

end

#-------------------------------------------------------------------------------------- 
# Translate wind direction in degress to text
#-------------------------------------------------------------------------------------- 
def Direction(degrees)

 if degrees <= 11
  dir = 'north'
 elsif degrees <= 33
  dir = 'north northeast'
 elsif degrees <= 56
  dir = 'northeast'
 elsif degrees <= 78
  dir = 'east northeast'
 elsif degrees <= 101
  dir = 'east'
 elsif degrees <= 123
  dir = 'east southeast'
 elsif degrees <= 146
  dir = 'southeast'
 elsif degrees <= 168
  dir = 'south southeast'
 elsif degrees <= 191
  dir = 'south'
 elsif degrees <= 213
  dir = 'south southwest'
 elsif degrees <= 236
  dir = 'southwest'
 elsif degrees <= 258
  dir = 'west southwest'
 elsif degrees <= 281
  dir = 'west'
 elsif degrees <= 303
  dir = 'west northwest'
 elsif degrees <= 326
  dir = 'northwest'
 elsif degrees <= 348
  dir = 'north northwest'
 else
  dir = 'north'
 end

 return dir

end