Asterisk CTI Implementation – Part One

Posted: November 2, 2015 in Asterisk

I am going to implement my Asterisk CTI class using the Asterisk REST Interface (ARI). The CTI class has different class objects, for example, a reactor object for websocket event notification, a websocket event handler object for receiving of Asterisk telephony events, a thread with a circular buffer to handle the telephony events, also a thread pool to deliver each telephony event to the calling application.

To get telephony events notification from Asterisk, we need to subscribe endpoint eventsource first. For testing #1, I have setup two extensions 1000 and 1002 in FreePBX, I subscribed evens for the two extensions using my class. I dialed 1002 from 1000, answered the call then hangup the call. I found a lot of events were generated from Asterisk and it seems there is no way to filter the number events from Asterisk. So I wrote a function to filter some unwanted events and remained the following events that are useful in my class

  • ChannelCreated
  • ChannelConnectedLine
  • ChannelDestroyed
  • ChannelStateChange
  • ChannelHold
  • ChannelUnhold
  • ChannelTalkingStarted
  • ChannelTalkingFinished
  • ChannelEnteredBridge
  • ChannelLeftBridge

After the filtering applied, the following events in blue are for extension 1000, while the events in red are for extension 1002.

{ “type”: “ChannelCreated”, “timestamp”: “2015-11-02T22:28:38.776+0800”, “channel”: { “id”: “1446474518.30”, “name”: “PJSIP/1000-00000006”, “state”: “Ring”, “caller”: { “name”: “device”, “number”: “1000” }, “connected”: { “name”: “”, “number”: “” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “1002”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.774+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelConnectedLine”, “timestamp”: “2015-11-02T22:28:38.862+0800”, “channel”: { “id”: “1446474518.30”, “name”: “PJSIP/1000-00000006”, “state”: “Ring”, “caller”: { “name”: “Kai”, “number”: “1000” }, “connected”: { “name”: “Ping”, “number”: “” }, “accountcode”: “”, “dialplan”: { “context”: “macro-dial-one”, “exten”: “s”, “priority”: 40 }, “creationtime”: “2015-11-02T22:28:38.774+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelCreated”, “timestamp”: “2015-11-02T22:28:38.864+0800”, “channel”: { “id”: “1446474518.32”, “name”: “PJSIP/1002-00000007”, “state”: “Down”, “caller”: { “name”: “device”, “number”: “1002” }, “connected”: { “name”: “”, “number”: “” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “s”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.864+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelConnectedLine”, “timestamp”: “2015-11-02T22:28:38.862+0800”, “channel”: { “id”: “1446474518.30”, “name”: “PJSIP/1000-00000006”, “state”: “Ring”, “caller”: { “name”: “Kai”, “number”: “1000” }, “connected”: { “name”: “Ping”, “number”: “1002” }, “accountcode”: “”, “dialplan”: { “context”: “macro-dial-one”, “exten”: “s”, “priority”: 41 }, “creationtime”: “2015-11-02T22:28:38.774+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelConnectedLine”, “timestamp”: “2015-11-02T22:28:38.865+0800”, “channel”: { “id”: “1446474518.32”, “name”: “PJSIP/1002-00000007”, “state”: “Down”, “caller”: { “name”: “device”, “number”: “1002” }, “connected”: { “name”: “Kai”, “number”: “1000” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “1002”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.864+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelStateChange”, “timestamp”: “2015-11-02T22:28:39.027+0800”, “channel”: { “id”: “1446474518.32”, “name”: “PJSIP/1002-00000007”, “state”: “Ringing”, “caller”: { “name”: “device”, “number”: “1002” }, “connected”: { “name”: “Kai”, “number”: “1000” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “1002”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.864+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelStateChange”, “timestamp”: “2015-11-02T22:28:45.637+0800”, “channel”: { “id”: “1446474518.32”, “name”: “PJSIP/1002-00000007”, “state”: “Up”, “caller”: { “name”: “device”, “number”: “1002” }, “connected”: { “name”: “Kai”, “number”: “1000” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “1002”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.864+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelStateChange”, “timestamp”: “2015-11-02T22:28:45.637+0800”, “channel”: { “id”: “1446474518.30”, “name”: “PJSIP/1000-00000006”, “state”: “Up”, “caller”: { “name”: “Kai”, “number”: “1000” }, “connected”: { “name”: “Ping”, “number”: “1002” }, “accountcode”: “”, “dialplan”: { “context”: “macro-dial-one”, “exten”: “s”, “priority”: 44 }, “creationtime”: “2015-11-02T22:28:38.774+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelDestroyed”, “timestamp”: “2015-11-02T22:28:50.080+0800”, “cause”: 16, “cause_txt”: “Normal Clearing”, “channel”: { “id”: “1446474518.30”, “name”: “PJSIP/1000-00000006”, “state”: “Up”, “caller”: { “name”: “Kai”, “number”: “1000” }, “connected”: { “name”: “Ping”, “number”: “1002” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “h”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.774+0800”, “language”: “en” }, “application”: “goanswer” }

{ “type”: “ChannelDestroyed”, “timestamp”: “2015-11-02T22:28:50.082+0800”, “cause”: 16, “cause_txt”: “Normal Clearing”, “channel”: { “id”: “1446474518.32”, “name”: “PJSIP/1002-00000007”, “state”: “Up”, “caller”: { “name”: “device”, “number”: “1002” }, “connected”: { “name”: “Kai”, “number”: “1000” }, “accountcode”: “”, “dialplan”: { “context”: “from-internal”, “exten”: “”, “priority”: 1 }, “creationtime”: “2015-11-02T22:28:38.864+0800”, “language”: “en” }, “application”: “goanswer” }

For testing #2, I used three extensions, they are 1000, 1002 and 1006. I initiated a call from 1000 to 1002, answered the call by 1002, then initiated another from 1000 to 1006, answered the call by 1006, the last step transferred the call by 1000, leaving 1002 and 1006 in a call, then ended the call by 1002 finally. The text file is here.

For testing #3, I used three extensions, they are 1000, 1002 and 1006. I initiated a call from 1000 to 1002, answered the call by 1002, then initiated another from 1000 to 1006, answered the call by 1006, the last step conference the call by 1000, and finally hangup the call by 1002. The text file is here.

Next step I will add call control functions such as Originate, Hold, Retrieve, Transfer and Conference, etc. After the implementation of the CTI class, I will port my tools such as ScreenPop, ivrSVR to support Asterisk. Good luck to me.

Comments
  1. hendrig says:

    hi, if we had 1000 extensions should we write each ext dial plan? so it should write 1000 dial plan?

  2. hendrig says:

    sorry i want build screen pop to CRM from asterisk
    the issue is i’m confuse how to get all event from asterisk , i tried configured extensions.conf using Statis but i just got json event when direct call to extension but when using IVR goes to queue i didnt got anything

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s