O'Reilly Emerging Telephony

oreilly.comSafari Books Online.Conferences.
advertisement
MySQL Conference and Expo April 14-17, 2008, Santa Clara, CA
AddThis Social Bookmark Button

Listen Print Discuss Subscribe to Telephony Subscribe to Newsletters

Fast Prototyping of Telephony Applications with YATE
Pages: 1, 2, 3

Another Trivial Example

Let's look at the keyecho toy ivr again, this time written with flow. First, the route generator directs calls to ivr to a dumb channel and starts the ivr generator for them. Notice that the ivr generator is started and forgotten (using go()). This allows the route generator to proceed to the next call.route message without delay:



    ...
    def route(yate):
        while True:
        yield yate.onmsg("call.route", lambda m : m["called"] == "ivr")
        route = getResult()
        go(ivr(yate, route["id"]))
        route.ret(True, "dumb/")
    ...

The ivr generator first answers the call, then handles chan.dtmf messages in a while loop, which is a more readable construct than adding callbacks to Deferreds. In the previous keyecho example, we ignored hangup events. In this example, chan.hangup is properly handled by the AbandonedException handler:

...
def ivr(yate, callid):
    try:
        end = yate.onwatch("chan.hangup", lambda m : m["id"] == callid)

        yield yate.onwatch("call.execute",
            lambda m : m["id"] == callid,
            until = end)
        execute = getResult()

        targetid = execute["targetid"]

        yate.msg("call.answered",
                 {"id": targetid,
                  "targetid": callid}).enqueue()

        print "Call %s answered." % callid

        while True:
            yield yate.onmsg(
                "chan.dtmf",
                lambda m : m["id"] == callid,
                end)
            dtmf = getResult()

            print "Dtmf %s received." % dtmf["text"]

            yate.msg("chan.masquerade",
                {"message" : "chan.attach",
                 "id": targetid,
                 "source": "wave/play/./sounds/digits/pl/%s.gsm" % \
                 dtmf["text"]}).enqueue()

            dtmf.ret(True)

    except AbandonedException, e:
        print "Call %s abandoned." % callid

...

A Non-Trivial Example

Keyecho is only a toy. Now let's look at a more realistic "blind transfer" example.

It's beyond the scope of this article to describe how a transfer is initiated using touch-tones on the handset. (The examples directory in YAYPM includes a complete example.) However, once a transfer is finally initiated, a call.execute message containing the CallEndPoint to be transferred is dispatched. The module that handles the target extension will treat it as a normal incoming call, and will connect its CallEndPoint and initiate a call:

def blind_transfer(yate, callid, targetid, transferto, returnto):
    try:
        yate.msg(
            "chan.masquerade",
            {"message" : "call.execute",                
             "id": targetid, "callto": transferto}).enqueue()

At this point, one of three things can happen: the endpoint being transferred hangs up, the call initiated by the target extension goes unanswered, or the call initiated by the target is answered:

        end = yate.onmsg(
             "chan.hangup",
             lambda m : m["id"] == targetid,
            autoreturn = True)

        notanswered =  yate.onmsg(
            "chan.disconnected",
            lambda m : m["id"] == targetid,
            until = end)

        answered =  yate.onwatch(
            "call.answered",
            lambda m : m["targetid"] == targetid,
            until = end)

In the case of the unanswered call, YATE sends a chan.disconnected message. This signals the last chance to reconnect the CallEndPoint.

Next, we use the YAYPM XOR function to combine the answered and unanswered Deferreds into a single Deferred that will fire when either of the two fires and automatically cancel the other one. getResult() returns both the index of the Deferred that fired and the return value of that Deferred. In the following code sample, if getResult returns an index of 0, it means that answered fired. If it returns an index of 1, it means that notanswered fired:

        yield XOR(answered, notanswered)
        what, m = getResult()

Since chan.hangup was used as an until condition by both the answered and unanswered Deferreds, getResult will raise AbandonedException if the extension to be transferred hangs up.

If answered fires, we simply have to return the call.answered message that YATE sent:

        if what == 0:
            logger.debug("Blind transfer to: %s done" % transferto)
            m.ret(False)    
            return
        else:

If notanswered fires, the following code tries get the routing module to resolve the returnto extension in order to return the call to be transferred to the transfer initiator. If routing is possible (i.e., the route message is processed), we then try to execute the connection back to the initiator. If all goes well, the CallEndPoint being transferred is connected to CallEndPoint of the initiator, and we can then return the chan.disconnected message:

            logger.debug(
                "Blind transfer to: %s failed. Returning to %s" % \
                (transferto, returnto))

            route = yate.msg("call.route",
                             {"called": returnto},
                             until = end)
            yield route.dispatch()

            if not getResult():
                logger.debug("Can't return to: %s" % returnto)
                m.ret(False)
                return

            yate.msg("chan.masquerade",
                     {"message" : "call.execute",                
                      "id": m["id"],
                      "callto": route.getRetValue(),
                      "called": returnto}).enqueue()
            yate.ret(m, True)

Not much can be done in the case of a hangup:

    except AbandonedException, e:
        logger.debug(
            "Blind transfer to: %s failed. Peer has disconnected" % \
            transferto)

Summary

YATE has a very minimalistic architecture and exposes most of its features through an external protocol. Since this external protocol is a simple text protocol, it can be written quickly in any language. There are libraries for Perl, PHP, and Python. At the moment, YAYPM is the most advanced YATE connector library. YAYPM uses the Twisted framework and Python generators to allow programmers to write prototypes rapidly. Since Twisted is a huge protocol library, it is easy to mix telephony applications with protocols such as http, smtp, and sql.

Resources

YAYPM
YATE main site
YATE messages
Twisted main site
Twisted O'Reilly book

Maciek Kaminski graduated from Warsaw University with a Computer Science degree.


Return to O'Reilly Emerging Telephony.


Comment on this article
You must be logged in to the O'Reilly Network to post a talkback.
Post Comment


Search Emerging Telephony

Search

Tagged Articles

Post to del.icio.us

This article has been tagged:

voip

Articles that share the tag voip:

Asterisk: A Bare-Bones VoIP Example (110 tags)

VoIP and POTS Integration with Asterisk (47 tags)

The PBX Is Dead; Long Live VoIP (42 tags)

Building Your Own Teleconference System with Asterisk and Gizmo (28 tags)

Top Ten Questions People Ask About Switching to Internet Telephones (23 tags)

View All

programming

Articles that share the tag programming:

Rolling with Ruby on Rails (1374 tags)

Very Dynamic Web Interfaces (279 tags)

Ajax on Rails (231 tags)

Understanding MVC in PHP (202 tags)

A Simpler Ajax Path (186 tags)

View All

telephony

Articles that share the tag telephony:

Asterisk: A Bare-Bones VoIP Example (36 tags)

The PBX Is Dead; Long Live VoIP (12 tags)

Top Ten Questions People Ask About Switching to Internet Telephones (9 tags)

Building Advanced Telecom Apps on a Shoestring (8 tags)

What Is Skype (4 tags)

View All

Sponsored Resources

  • Inside Lightroom

Related to this Article

Twisted Network Programming Essentials Twisted Network Programming Essentials
by Abe Fettig
October 2005
$29.95 USD

Take Control of Your iPhone Apps Take Control of Your iPhone Apps
by Jeff Carlson
November 2009
$10.00 USD

Advertisement
O'Reilly Media

©2009, O'Reilly Media, Inc.
(707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
About O'Reilly
Academic Solutions
Authors
Contacts
Customer Service
Jobs
Newsletters
O'Reilly Labs
Press Room
Privacy Policy
RSS Feeds
Terms of Service
User Groups
Writing for O'Reilly
Content Archive
Business Technology
Computer Technology
Google
Microsoft
Mobile
Network
Operating System
Digital Photography
Programming
Software
Web
Web Design
More O'Reilly Sites
O'Reilly Radar
Ignite
Tools of Change for Publishing
Digital Media
Inside iPhone
makezine.com
craftzine.com
hackszine.com
perl.com
xml.com

Partner Sites
InsideRIA
java.net
O'Reilly Insights on Forbes.com