<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 14 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
        {mso-style-priority:34;
        margin-top:0in;
        margin-right:0in;
        margin-bottom:0in;
        margin-left:.5in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";}
span.EmailStyle17
        {mso-style-type:personal-compose;
        font-family:"Calibri","sans-serif";
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri","sans-serif";}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
/* List Definitions */
@list l0
        {mso-list-id:1806194682;
        mso-list-type:hybrid;
        mso-list-template-ids:1365172006 1873438634 67698691 67698693 67698689 67698691 67698693 67698689 67698691 67698693;}
@list l0:level1
        {mso-level-start-at:0;
        mso-level-number-format:bullet;
        mso-level-text:-;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:"Calibri","sans-serif";
        mso-fareast-font-family:Calibri;}
@list l0:level2
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:"Courier New";}
@list l0:level3
        {mso-level-number-format:bullet;
        mso-level-text:\F0A7;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:Wingdings;}
@list l0:level4
        {mso-level-number-format:bullet;
        mso-level-text:\F0B7;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:Symbol;}
@list l0:level5
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:"Courier New";}
@list l0:level6
        {mso-level-number-format:bullet;
        mso-level-text:\F0A7;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:Wingdings;}
@list l0:level7
        {mso-level-number-format:bullet;
        mso-level-text:\F0B7;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:Symbol;}
@list l0:level8
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:"Courier New";}
@list l0:level9
        {mso-level-number-format:bullet;
        mso-level-text:\F0A7;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:Wingdings;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal">I’ve spent some time the last couple days working on a way to get presence to work with vanilla Asterisk (10.1.2) and a func_odbc hotdesking deployment. Given the lack of much results of anything on Google for this type of deployment,
I thought I’d share my dialplan magic in case anyone else needs to find the same solution in the future.
<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">And if someone sees something I’m doing that can/should be improved, I’d be happy to know that too…<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">My dialplan setup is based heavily on the example in the Relational Database Integration chapter of the most recent verison of Asterisk: The Definitive Guide (<a href="http://ofps.oreilly.com/titles/9780596517342/asterisk-DB.html">http://ofps.oreilly.com/titles/9780596517342/asterisk-DB.html</a>).
I’m also using queues (subscribing the SIP phone itself and grabbing the Caller ID name of the logged in user for the agent label), so there’s some extra excitement related to all that.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Basically, the premise is…<o:p></o:p></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo1"><![if !supportLists]><span style="mso-list:Ignore">-<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]>When an internal user dials a call, set a GROUP(activecalls) variable on their channel to their extension, and set their custom device state to INUSE.
<o:p></o:p></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo1"><![if !supportLists]><span style="mso-list:Ignore">-<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]>When an internal user is dialed, set their custom device state to either RINGINUSE or RINGING, depending on if their extension is or isn’t involved in another call.
<o:p></o:p></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo1"><![if !supportLists]><span style="mso-list:Ignore">-<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]>When an internal user answers a dialed call, set a GROUP(activecalls) variable to their extension and set their custom device state to INUSE.<o:p></o:p></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo1"><![if !supportLists]><span style="mso-list:Ignore">-<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]>When an agent answers a queue call, set a GROUP(activecalls) variable to their extension and set their custom device state to INUSE.<o:p></o:p></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo1"><![if !supportLists]><span style="mso-list:Ignore">-<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]>When any call hangs up, do the following:<o:p></o:p></p>
<p class="MsoListParagraph" style="margin-left:1.0in;text-indent:-.25in;mso-list:l0 level2 lfo1">
<![if !supportLists]><span style="font-family:"Courier New""><span style="mso-list:Ignore">o<span style="font:7.0pt "Times New Roman"">
</span></span></span><![endif]>Check to see if the call was a queue call (MEMBERINTERFACE will be set). If it was, figure out which extension answered it, and check to see how many GROUP(activecalls) they’re on. If it’s equal to 1, that means the call they’re
finishing is the only call they’re on, so set the custom device state to NOT_INUSE. Otherwise set (should really wind up effectively being “leave”) the device state to INUSE, since they’ve got at least 1 other call in progress.<o:p></o:p></p>
<p class="MsoListParagraph" style="margin-left:1.0in;text-indent:-.25in;mso-list:l0 level2 lfo1">
<![if !supportLists]><span style="font-family:"Courier New""><span style="mso-list:Ignore">o<span style="font:7.0pt "Times New Roman"">
</span></span></span><![endif]>Check to see if the call was a call dialed by an internal user (WHO will be set). If it was, check to see how many GROUP(activecalls) the WHO user is on. If it’s equal to 1, that means the call they’re finishing is the only
call they’re on, so set the custom device state to NOT_INUSE. Otherwise set (should really wind up effectively being “leave”) the device state to INUSE, since they’ve got at least 1 other call in progress.<o:p></o:p></p>
<p class="MsoListParagraph" style="margin-left:1.0in;text-indent:-.25in;mso-list:l0 level2 lfo1">
<![if !supportLists]><span style="font-family:"Courier New""><span style="mso-list:Ignore">o<span style="font:7.0pt "Times New Roman"">
</span></span></span><![endif]>Check to see if the call was a call directly dialed to an internal user (E will be set). If it was, check to see how many GROUP(activecalls) the E user is on. If it’s equal to 1, that means the call they’re finishing is the
only call they’re on, so set the custom device state to NOT_INUSE. Otherwise set (should really wind up effectively being “leave”) the device state to INUSE, since they’ve got at least 1 other call in progress.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Still haven’t figured out how to get FOP2 to work with my Custom devices, working on that. A known limitation of this approach is that my custom devices won’t show ringing while a queue call is ringing them.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">This depends on “setinterfacevar=yes” being applied to all of my queues in queues.conf<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">The relevant portions of my dialplan are:<o:p></o:p></p>
<p class="MsoNormal">[hints]<o:p></o:p></p>
<p class="MsoNormal">exten => <my extension pattern match>,hint,Custom:${EXTEN}<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[hotdesk_outbound]<o:p></o:p></p>
<p class="MsoNormal">includes (via cascade) internal-calls<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">exten => .X,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">…snip do stuff to determine who’s calling, set their extension number to WHO…<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(GROUP(activecalls)=${WHO})<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(DEVICE_STATE(Custom:${WHO})=INUSE)<o:p></o:p></p>
<p class="MsoNormal">…snip make the call…<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">exten => h,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same => n,Goto(cleanup-devicestate,s,1)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[internal-calls]<o:p></o:p></p>
<p class="MsoNormal">…snip set the called extension to E and look up what device to dial…<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Who: ${WHO} Calls: ${GROUP_COUNT(${WHO}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Exten: ${E} Calls: ${GROUP_COUNT(${E}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,ExecIf($[${GROUP_COUNT(${E}@activecalls)}>0]?Set(DEVICE_STATE(Custom:${E})=RINGINUSE):Set(DEVICE_STATE(Custom:${E})=RINGING))<o:p></o:p></p>
<p class="MsoNormal">same => n,Dial(SIP/${USER_LOCATION},20,wWU(answered^${E}))<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[incoming]<o:p></o:p></p>
<p class="MsoNormal">…snip call menu eventually puts callers into something like…<o:p></o:p></p>
<p class="MsoNormal">same => n,Queue(<myqueuename>,<options>,,,<timeout>,,,answered-queue)<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Who: ${WHO} Calls: ${GROUP_COUNT(${WHO}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Exten: ${E} Calls: ${GROUP_COUNT(${E}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,ExecIf($[${GROUP_COUNT(${E}@activecalls)}>0]?Set(DEVICE_STATE(Custom:${E})=RINGINUSE):Set(DEVICE_STATE(Custom:${E})=RINGING))<o:p></o:p></p>
<p class="MsoNormal">same => n,Dial(SIP/${USER_LOCATION},20,wWU(answered^${E}))<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">…snip dial my internal extensions directly – set the dialed extension to E and look up the device to dial…<o:p></o:p></p>
<p class="MsoNormal">same => n,ExecIf($[${GROUP_COUNT(${E}@activecalls)}>0]?Set(DEVICE_STATE(Custom:${E})=RINGINUSE):Set(DEVICE_STATE(Custom:${E})=RINGING))<o:p></o:p></p>
<p class="MsoNormal">same => n,Dial(SIP/${USER_LOCATION},20,wWU(answered^${E}))<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">exten => h,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same => n,Goto(cleanup-devicestate,s,1)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[answered]<o:p></o:p></p>
<p class="MsoNormal">exten => s,1,NoOp(${ARG1})<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(GROUP(activecalls)=${ARG1})<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Exten: ${ARG1} Calls: ${GROUP_COUNT(${ARG1}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(DEVICE_STATE(Custom:${ARG1})=INUSE)<o:p></o:p></p>
<p class="MsoNormal">same => n,Return()<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[answered-queue]<o:p></o:p></p>
<p class="MsoNormal">exten => s,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(AGENT=${HOTDESK_PHONE_STATUS(${CUT(MEMBERINTERFACE,/,2)})})<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Agent ${AGENT}) ; for my troubleshooting to see what it’s doing<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(GROUP(activecalls)=${AGENT})<o:p></o:p></p>
<p class="MsoNormal">same => n,Return()<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">[cleanup-devicestate]<o:p></o:p></p>
<p class="MsoNormal">exten => s,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Who: ${WHO} Calls: ${GROUP_COUNT(${WHO}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Exten: ${E} Calls: ${GROUP_COUNT(${E}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,GotoIf($[${ISNULL(${MEMBERINTERFACE})}]?internal)<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(DEVICE=${CUT(MEMBERINTERFACE,/,2)})<o:p></o:p></p>
<p class="MsoNormal">same => n,Set(AGENT=${HOTDESK_PHONE_STATUS(${DEVICE})})<o:p></o:p></p>
<p class="MsoNormal">same => n,NoOp(Agent: ${AGENT} Calls: ${GROUP_COUNT(${AGENT}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same => n,ExecIf($[${GROUP_COUNT(${AGENT}@activecalls)}>1]?Set(DEVICE_STATE(Custom:${AGENT})=INUSE):Set(DEVICE_STATE(Custom:${AGENT})=NOT_INUSE))<o:p></o:p></p>
<p class="MsoNormal">same => n(internal),ExecIf(${ISNULL(${WHO})}?NoOp(${HOTDESK_INFO(location,${WHO})}):ExecIf($[${GROUP_COUNT(${WHO}@activecalls)}>1]?Set(DEVICE_STATE(Custom:${WHO})=INUSE):Set(DEVICE_STATE(Custom:${WHO})=NOT_INUSE)))<o:p></o:p></p>
<p class="MsoNormal">same => n,ExecIf(${ISNULL(${E})}?NoOp(${HOTDESK_INFO(location,${E})}):ExecIf($[${GROUP_COUNT(${E}@activecalls)}>1]?Set(DEVICE_STATE(Custom:${E})=INUSE):Set(DEVICE_STATE(Custom:${E})=NOT_INUSE)))<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Noah Engelberth<o:p></o:p></p>
<p class="MsoNormal">MetaLINK Technologies<o:p></o:p></p>
<p class="MsoNormal">System Administration<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
</body>
</html>