One of the most requested functionality prior to Asterisk 1.2 was shared line appearance. It finally appeared in 1.2 but it’s take-up has been somewhat muted. Part of the reason for this is that it uses conferencing and while it works as expected with Line appearance, you can’t use the transfer buttons on the phones. In this article, Nik Middleton explores an alternative approach.
As mentioned in the intro, the embeded SLA functionality in Asterisk uses conferencing in order to achieve its goals. Conferencing is a drain on the CPU and in addition requires a timing source such as a digium card, or failing that the use of ZTDummy. In addition, because you are in a conference, you need to use the native Asterisk transfer functionality (normally set to # in features.conf)
So, given the above, it’s not surprising that SLA hasn’t had a high adoption rate, yes the lights flash for the appropriate lines, but you can’t use your BLF or speeddials to transfer calls. Essentially all the fancy features on your phone are useless.
The goals
- Replicate the line indications
- Retain all of the genric functionality of the phone with regards to call transfer etc.
How it’s going to work from the user’s perspective
If a user hits a ‘Line button’, they’ll get an external dial tone. At the same time on all of the other phones, that line will show as ‘IN USE’
Hitting a speed dial, will ‘Grab’ an available LINE if it’s an external call and reflect as above
Dialing an external extension will NOT take an external line
External calls on hold, will reflect that status on the other handsets. Selecting that line will pickup the live call.
The solution
We’re going to make use of the DEVSTATE function which has a back port to 1.4, DISA and hints for this exercise.
Virtual Lines
We introduce the concept of virtual lines here. Why? Well certainly with SIP, they’re not real anyway are they? What about the fact that I might have an ISDN 30 PRI? Well once again, we really don’t care what physical line we’re actually on, we just need to light the appropriate lamp on the phones. Don’t worry, it’ll become clearer later, however, by using the concept of virtual lines, we abstract ourselves for the physical layer, we allows us to combine both ISDN for the sake of argument, and SIP trunks into virtual lines.
Hints
First, we need to setup hints. You might wonder why we’re using something like 8*200_line1 well it’s because we need to make it un-dialable.
The mysubscribes section below sets up the hints for the virtual lines. Devstate allows one not only to monitor actual devices, but also virtual devices as well. In otherwords you can use a variable to both read and write states to. The keyword ‘Custom’ denotes this is not a physical device
| [mysubscribes] exten => 8*200_line1,hint,Custom:vline_1 exten => 8*200_line2,hint,Custom:vline_2 exten => 8*201_line1,hint,Custom:vline_1 exten => 8*201_line2,hint,Custom:vline_2 |
In the above section we have 4 hints setup. Extension 200 and 201 are monitoring the hints for vline1&2. Within the speeddial section on the phone, we’ve added 8*200_line1 for line one on extension 200, Line two has a value of 8*200_line2. The same applies to extension 201.
Phone setup
Clearly, in this example you need to have a SIP phone that is capable of displaying BLF.
The two top buttons where used, but rather than making them line keys, they were assigned with a function type of BLF (busy lamp flag)
Extensions 200 and 201 were set as follows:
BLF1: 8*200_line1
BLF2: 8*200_line2
BLF1: 8*201_line1
BLF2: 8*201_line2
Call Origination
In our example, the Sip phones have a conext set in Sip.conf as being sip-out. This means that any dialing action will take place within that context.
| [sip-out]
exten => 8*200_line1,1,gotoif($[${DEVSTATE(Custom:vline_1)} = NOT_INUSE]?10:) |
Here, we’re checking if vline_1 is in use or not. All we care about is if it’s free. If it’s any other state we can’t use it. If it’s available, we jump to [line1-out] context.
The reason for using a different context is because we need to track which line to set when we get a hangup.
Line out context
| [Line1-out]
exten => sla,1,Set(DEVSTATE(Custom:vline_1)=INUSE) |
When we enter this context, we first set the virtual line to INUSE, this prevents other users taking this line and at the same time shows other phones that the line is taken
The next line provides an outside dial tone. Within this context, we’re only showing one pattern matching sequence, in that the user needs to dial 7XXXX, at which point the call will go out via a virtual line 1 SIP trunk, but this could equally be an ISDN channel.
Finally on hangup we set the correct virtual line to be NOT_INUSE. As mentioned previously, without being in a specific context, we wouldn’t know which line to reset.
Call Reception
In our example, inbound calls are set to come in via the [from-sip] context and are presented as being to 99102.
| [from-sip]
exten => 99102,1,Noop(inbound sip call) exten => 99102,10,goto(line1-in,sla,1) |
As the call comes in, we check to see what available virtual lines we have. If there are non available, we reject the call. In the above example it’s rather crude, but in reality we would reject the call at the network layer so the caller would not be billed for a busy call.
Depending on which line was free, we jump to the appropriate [LineX-in] context, once again, this is so that we know which line to reset on hangup
| [Line1-in]
exten => sla,1,Noop() exten => h,1,Set(DEVSTATE(Custom:vline_1)=NOT_INUSE) |
The first thing we do is set the line state to Ringing and then immediately dial the extension(s). Of particular note here is the Macro on the Dial function. This macro gets executed when the call gets bridged, i.e. answered.
| [macro-line1-in]
exten => s,1,Noop(In Line 1 Macro) |
All this macro does is set the virtual line state to in use.
Full Source Code for SLA Example
; extensions.conf – the Asterisk dial plan
;
;autofallthrough=no
[default]
;Initialise state on startup
exten => s,1,Set(DEVSTATE(Custom:vline_1)=NOT_INUSE)
exten => s,2,Set(DEVSTATE(Custom:vline_2)=NOT_INUSE)
[mysubscribes]
exten => 8*26_line1,hint,Custom:vline_1
exten => 8*26_line2,hint,Custom:vline_2
exten => 8*27_line1,hint,Custom:vline_1
exten => 8*27_line2,hint,Custom:vline_2
[from-sip]
;see what virtual lines are available
exten => 93102,1,Noop(inbound sip call)
exten => 93102,2,gotoif($[${DEVSTATE(Custom:vline_1)} = NOT_INUSE]?10:)
exten => 93102,3,gotoif($[${DEVSTATE(Custom:vline_2)} = NOT_INUSE]?20:)
exten => 93102,4,ANSWER()
exten => 93102,5,busy(5)
exten => 93102,6,hangup()
exten => 93102,10,goto(line1-in,sla,1)
exten => 93102,20,goto(line2-in,sla,1)
[sip-out]
exten => 8*26_line1,1,gotoif($[${DEVSTATE(Custom:vline_1)} = NOT_INUSE]?10:)
exten => 8*26_line1,n,congestion(15)
exten => 8*26_line1,n,hangup()
exten => 8*26_line1,10,goto(line1-out,sla,1)
exten => 8*26_line2,1,gotoif($[${DEVSTATE(Custom:vline_2)} = NOT_INUSE]?10:)
exten => 8*26_line2,n,congestion(15)
exten => 8*26_line2,n,hangup()
exten => 8*26_line2,10,goto(line2-out,sla,1)
exten => 8*27_line1,1,gotoif($[${DEVSTATE(Custom:vline_1)} = NOT_INUSE]?10:)
exten => 8*27_line1,n,congestion(15)
exten => 8*27_line1,n,hangup()
exten => 8*27_line1,10,goto(line1-out,sla,1)
exten => 8*27_line2,1,gotoif($[${DEVSTATE(Custom:vline_2)} = NOT_INUSE]?10:)
exten => 8*27_line2,n,congestion(15)
exten => 8*27_line2,n,hangup()
exten => 8*27_line2,10,goto(line2-out,sla,1)
exten => i,1,Answer()
exten => i,2,playback(invalid)
exten => i,3,Hangup()
[line1-out]
exten => sla,1,Noop()
exten => sla,n,Set(DEVSTATE(Custom:vline_1)=INUSE)
exten => sla,n(disa),Disa(no-password|line1-out)
exten => sla,n,hangup()
exten => _9XXXX,1,DIAL(SIP/${EXTEN}@93102,40,w)
exten => h,1,Set(DEVSTATE(Custom:vline_1)=NOT_INUSE)
[line2-out]
exten => sla,1,Noop()
exten => sla,n,Set(DEVSTATE(Custom:vline_2)=INUSE)
exten => sla,n(disa),Disa(no-password|line2-out)
exten => sla,n,hangup()
exten => _9XXXX,1,DIAL(SIP/${EXTEN}@93102,40,w)
exten => h,1,Set(DEVSTATE(Custom:vline_2)=NOT_INUSE)
[line1-in]
exten => sla,1,Noop()
exten => sla,n,Set(DEVSTATE(Custom:vline_1)=RINGING)
exten => sla,n,dial(sip/26&sip/27,,M(line1-in))
exten => h,1,Set(DEVSTATE(Custom:vline_1)=NOT_INUSE)
[line2-in]
exten => sla,1,Noop()
exten => sla,n,set(DEVSTATE(Custom:vline_2)=RINGING)
exten => sla,n,dial(sip/26&sip/27,,M(line2-in))
exten => h,1,Set(DEVSTATE(Custom:vline_2)=NOT_INUSE)
;====================================================
[macro-line1-in]
exten => s,1,Noop(In Line 1 Macro)
exten => s,n,Set(DEVSTATE(Custom:vline_1)=INUSE)
[macro-line2-in]
exten => s,1,Noop(In Line 2 Macro)
exten => s,n,Set(DEVSTATE(Custom:vline_2)=INUSE)
Hi,
Looks very interesting !
My “wish” is a little bit different :
Each of my groups is a call from another city agency.
So I would like to configure the line buttons on my SP962 phone to see the status of my groups/queues = call from which city.
It should be like that (work like that with a SPA9000):
When there is an incoming call to a groups/queues, the line button on my SPA962 (with SLA to the group) should show me there is an incoming call (i.e from L.A.) to this group.
On the line buttons on SPA932 sidecar I can see which phones ringing or took the call.
How can I make this ?
In my case I would like to see the status of the group.
I do not know how to configure the SLA for this on my phones.
The “normal” SLA works for extensions on the phones connected to Asterisk.