AGI Scripting with Python

AGI stands for Asterisk Gateway Interface. It is very similar to CGI (common gateway interface) which is one of the first forms of web development.

AGI allows for Asterisk to communicate in real-time with an external process. Asterisk will send output to the external process through “Standard Out” (STDOUT), and will listen for input on “Standard In” (STDIN). STDOUT and STDIN are what you use whenever you are interacting with the command line. When we type “ls” in the terminal we are sending in that command to the terminal process through its STDIN, and when the terminal shows us the list of files it is sending that list to our screen through its STDOUT.

Calling AGI Scripts

To call an AGI script from the Asterisk dialplan you use the AGI command

exten => s,1,Answer();
exten => s,n,AGI(/home/ck987/asterisk_agi/agi_test.py);

By default Asterisk looks for the AGI script in the agi-bin (/var/lib/asterisk/agi-bin). You can place them anywhere but my instructions will assume they’re in your asterisk_agi directory.

Debugging

In the Asterisk administration console you can type: “agi set debug on” to enable AGI debugging.  “agi set debug off” will turn off AGI debugging.

Basic AGI Scripting in Python

#!/usr/bin/env python
import sys
from asterisk.agi import *

agi = AGI()
agi.verbose("python agi started")
callerId = agi.env['agi_callerid']
agi.verbose("call from %s" % callerId)
while True:
  agi.stream_file('vm-extension')
  result = agi.wait_for_digit(-1)
  agi.verbose("got digit %s" % result)
  if result.isdigit():
    agi.say_number(result)
  else:
   agi.verbose("bye!")
   agi.hangup()
   sys.exit()

Upload, Test and Run

Save the above script to a file, let’s call it “easy.py” for now. Using Fetch or another SFTP application upload the script to the “asterisk_agi” in your home directory. Log into the server using SSH and navigate to the directory containing the agi scripts (cd asterisk_agi) and execute the command required to make the file executable (chmod 755 easy.py).

To test that the file runs without error, type the following:

./easy.py

This should start the execution of the file, if it doesn’t there is an error that you need to fix. The application should wait indefinitely for input (which it is expecting from Asterisk through STDIN). You can simulate this by hitting return a couple of times. You should see some semblance of the commands that are sent to Asterisk via STDOUT.

The Asterisk AGI library I chose for Python was pyst2, which is actively maintained but has little documentation.  Your best bet is to look at the source code here: https://github.com/rdegges/pyst2

More Fun Get the current weather!

#!/usr/bin/env python
import urllib2
import xml.etree.ElementTree as ET
import sys
from asterisk.agi import *

agi = AGI()
# for a complete list of US cities, go to
# http://www.weather.gov/xml/current_obs/
weatherURL = []
weatherURL.append("http://w1.weather.gov/xml/current_obs/KNYC.xml") #NYC
weatherURL.append("http://w1.weather.gov/xml/current_obs/KJFK.xml") #JFK
weatherURL.append("http://w1.weather.gov/xml/current_obs/KART.xml") #Watertown, NY
weatherURL.append("http://w1.weather.gov/xml/current_obs/KBGM.xml") #Binghamton
weatherURL.append("http://w1.weather.gov/xml/current_obs/KBUF.xml") #Buffalo
weatherURL.append("http://w1.weather.gov/xml/current_obs/KDKK.xml") #Chautauqua County
weatherURL.append("http://w1.weather.gov/xml/current_obs/KDSV.xml") #Dansville
weatherURL.append("http://w1.weather.gov/xml/current_obs/KELM.xml") #Corning, NY
weatherURL.append("http://w1.weather.gov/xml/current_obs/KHPN.xml") #Westchester
weatherURL.append("http://w1.weather.gov/xml/current_obs/KFRG.xml") #Farmingdale, NY

while True:
  agi.stream_file('vm-extension')
  result = agi.wait_for_digit(-1)
  if result.isdigit():
   request = urllib2.Request(weatherURL[int(result)], headers={"Accept" : "application/xml"})
   xml = urllib2.urlopen(request)
   tree = ET.parse(xml)
   root = tree.getroot()
   temp_f = root.find('temp_f').text
   agi.say_number(temp_f)
  else:
   sys.exit()