<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&#8217;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.&nbsp; Given the lack of much results of anything on Google for this type of deployment,
 I thought I&#8217;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>&nbsp;</o:p></p>
<p class="MsoNormal">And if someone sees something I&#8217;m doing that can/should be improved, I&#8217;d be happy to know that too&#8230;<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</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>).&nbsp;
 I&#8217;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&#8217;s some extra excitement related to all that.<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Basically, the premise is&#8230;<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 &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</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.&nbsp;
<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 &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</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&#8217;t involved in another call.&nbsp;
<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 &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</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 &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</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 &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</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:&quot;Courier New&quot;"><span style="mso-list:Ignore">o<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;
</span></span></span><![endif]>Check to see if the call was a queue call (MEMBERINTERFACE will be set).&nbsp; If it was, figure out which extension answered it, and check to see how many GROUP(activecalls) they&#8217;re on.&nbsp; If it&#8217;s equal to 1, that means the call they&#8217;re
 finishing is the only call they&#8217;re on, so set the custom device state to NOT_INUSE.&nbsp; Otherwise set (should really wind up effectively being &#8220;leave&#8221;) the device state to INUSE, since they&#8217;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:&quot;Courier New&quot;"><span style="mso-list:Ignore">o<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;
</span></span></span><![endif]>Check to see if the call was a call dialed by an internal user (WHO will be set).&nbsp; If it was, check to see how many GROUP(activecalls) the WHO user is on.&nbsp; If it&#8217;s equal to 1, that means the call they&#8217;re finishing is the only
 call they&#8217;re on, so set the custom device state to NOT_INUSE.&nbsp; Otherwise set (should really wind up effectively being &#8220;leave&#8221;) the device state to INUSE, since they&#8217;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:&quot;Courier New&quot;"><span style="mso-list:Ignore">o<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;
</span></span></span><![endif]>Check to see if the call was a call directly dialed to an internal user (E will be set). &nbsp;If it was, check to see how many GROUP(activecalls) the E user is on.&nbsp; If it&#8217;s equal to 1, that means the call they&#8217;re finishing is the
 only call they&#8217;re on, so set the custom device state to NOT_INUSE.&nbsp; Otherwise set (should really wind up effectively being &#8220;leave&#8221;) the device state to INUSE, since they&#8217;ve got at least 1 other call in progress.<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Still haven&#8217;t figured out how to get FOP2 to work with my Custom devices, working on that.&nbsp; A known limitation of this approach is that my custom devices won&#8217;t show ringing while a queue call is ringing them.<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">This depends on &#8220;setinterfacevar=yes&#8221; being applied to all of my queues in queues.conf<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</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 =&gt; &lt;my extension pattern match&gt;,hint,Custom:${EXTEN}<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</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>&nbsp;</o:p></p>
<p class="MsoNormal">exten =&gt; .X,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">&#8230;snip do stuff to determine who&#8217;s calling, set their extension number to WHO&#8230;<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(GROUP(activecalls)=${WHO})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(DEVICE_STATE(Custom:${WHO})=INUSE)<o:p></o:p></p>
<p class="MsoNormal">&#8230;snip make the call&#8230;<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">exten =&gt; h,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Goto(cleanup-devicestate,s,1)<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">[internal-calls]<o:p></o:p></p>
<p class="MsoNormal">&#8230;snip set the called extension to E and look up what device to dial&#8230;<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Who: ${WHO} Calls: ${GROUP_COUNT(${WHO}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Exten: ${E} Calls: ${GROUP_COUNT(${E}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,ExecIf($[${GROUP_COUNT(${E}@activecalls)}&gt;0]?Set(DEVICE_STATE(Custom:${E})=RINGINUSE):Set(DEVICE_STATE(Custom:${E})=RINGING))<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Dial(SIP/${USER_LOCATION},20,wWU(answered^${E}))<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">[incoming]<o:p></o:p></p>
<p class="MsoNormal">&#8230;snip call menu eventually puts callers into something like&#8230;<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Queue(&lt;myqueuename&gt;,&lt;options&gt;,,,&lt;timeout&gt;,,,answered-queue)<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Who: ${WHO} Calls: ${GROUP_COUNT(${WHO}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Exten: ${E} Calls: ${GROUP_COUNT(${E}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,ExecIf($[${GROUP_COUNT(${E}@activecalls)}&gt;0]?Set(DEVICE_STATE(Custom:${E})=RINGINUSE):Set(DEVICE_STATE(Custom:${E})=RINGING))<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Dial(SIP/${USER_LOCATION},20,wWU(answered^${E}))<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">&#8230;snip dial my internal extensions directly &#8211; set the dialed extension to E and look up the device to dial&#8230;<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,ExecIf($[${GROUP_COUNT(${E}@activecalls)}&gt;0]?Set(DEVICE_STATE(Custom:${E})=RINGINUSE):Set(DEVICE_STATE(Custom:${E})=RINGING))<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Dial(SIP/${USER_LOCATION},20,wWU(answered^${E}))<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">exten =&gt; h,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Goto(cleanup-devicestate,s,1)<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">[answered]<o:p></o:p></p>
<p class="MsoNormal">exten =&gt; s,1,NoOp(${ARG1})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(GROUP(activecalls)=${ARG1})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Exten: ${ARG1} Calls: ${GROUP_COUNT(${ARG1}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(DEVICE_STATE(Custom:${ARG1})=INUSE)<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Return()<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">[answered-queue]<o:p></o:p></p>
<p class="MsoNormal">exten =&gt; s,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(AGENT=${HOTDESK_PHONE_STATUS(${CUT(MEMBERINTERFACE,/,2)})})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Agent ${AGENT}) ; for my troubleshooting to see what it&#8217;s doing<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(GROUP(activecalls)=${AGENT})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Return()<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">[cleanup-devicestate]<o:p></o:p></p>
<p class="MsoNormal">exten =&gt; s,1,NoOp()<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Who: ${WHO} Calls: ${GROUP_COUNT(${WHO}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Exten: ${E} Calls: ${GROUP_COUNT(${E}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,GotoIf($[${ISNULL(${MEMBERINTERFACE})}]?internal)<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(DEVICE=${CUT(MEMBERINTERFACE,/,2)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,Set(AGENT=${HOTDESK_PHONE_STATUS(${DEVICE})})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,NoOp(Agent: ${AGENT} Calls: ${GROUP_COUNT(${AGENT}@activecalls)})<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,ExecIf($[${GROUP_COUNT(${AGENT}@activecalls)}&gt;1]?Set(DEVICE_STATE(Custom:${AGENT})=INUSE):Set(DEVICE_STATE(Custom:${AGENT})=NOT_INUSE))<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n(internal),ExecIf(${ISNULL(${WHO})}?NoOp(${HOTDESK_INFO(location,${WHO})}):ExecIf($[${GROUP_COUNT(${WHO}@activecalls)}&gt;1]?Set(DEVICE_STATE(Custom:${WHO})=INUSE):Set(DEVICE_STATE(Custom:${WHO})=NOT_INUSE)))<o:p></o:p></p>
<p class="MsoNormal">same =&gt; n,ExecIf(${ISNULL(${E})}?NoOp(${HOTDESK_INFO(location,${E})}):ExecIf($[${GROUP_COUNT(${E}@activecalls)}&gt;1]?Set(DEVICE_STATE(Custom:${E})=INUSE):Set(DEVICE_STATE(Custom:${E})=NOT_INUSE)))<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</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>&nbsp;</o:p></p>
</div>
</body>
</html>