Skip to content

Instantly share code, notes, and snippets.

@lgaetz
Last active April 27, 2025 15:24
Show Gist options
  • Save lgaetz/4c14829c14eb0a739a216016d9f394b2 to your computer and use it in GitHub Desktop.
Save lgaetz/4c14829c14eb0a739a216016d9f394b2 to your computer and use it in GitHub Desktop.
Feature code to seamlessly bounce an active bridged call between a user’s multiple devices
; FreePBX dialplan for a feature code that can be dialed from an idle device that will immediately bounce
; any active call from any of the the same user's extensions. Primary use case is when a user has multiple
; registered devices they can quickly move the active call between deskphone, to mobile client to desktop client.
;
; License: GNU GPL3+
; latest version: https://gist.github.com/lgaetz/4c14829c14eb0a739a216016d9f394b2
;
; Requirements: FreeBPX 17+ or system based on version 17+
; Tested with Asterisk 20, will will likely work on any supported version
;
; version history 2024-03-22 First commit working
; 2024-03-24 Fix hint and change ext away from _.
; 2025-04-18 Updated for FreePBX 17
[from-internal-custom]
exten => _*37!,hint,PJSIP/${EXTEN:3}&PJSIP/98${EXTEN:3}&PJSIP/99${EXTEN:3}
exten => _*37!,1,Noop(User defined context to seize any active call from dialing user)
exten => _*37!,n,Gosub(macro-user-callerid,s,1())
exten => _*37!,n,Goto(seize-current-active-call,${AMPUSER},1)
[seize-current-active-call]
exten => _X.,1,Noop(User defined context seize-current-active-call to seize any active call from the dialing user)
; get full list of dialable devices for the user from AstDB
exten => _X.,n,Set(DEVS=${DB(AMPUSER/${AMPUSER}/device)}) ; & delimited list of devices
exten => _X.,n,Set(DEVS=${STRREPLACE(DEVS,&,\,)}) ; comma delimited list of devices
; step thru each device and look for an active channel
exten => _X.,n,While($["${SET(DEV=${POP(DEVS)})}" != ""])
exten => _X.,n,noop(device: ${DEV})
; using a regex of SIP/${EXTEN}- will match both SIP and PJSIP channels, and the trailing - character should
; help to ensure there is only a single match.
exten => _X.,n,set(seize_target=${CHANNELS(SIP/${DEV}-)})
; we don't want the dialing channel to seize itself, so remove current dialing channel from channel list and trim string
exten => _X.,n,Noop(Current channel name: ${CHANNEL})
exten => _X.,n,set(seize_target=${STRREPLACE(seize_target,${CHANNEL},)})
exten => _X.,n,set(seize_target=${STRREPLACE(seize_target, ,)})
; if an active channel is found exit the loop, otherwise loop again
exten => _X.,n,ExecIf($["${seize_target}"!=""]?ExitWhile)
exten => _X.,n,EndWhile()
exten => _X.,n,Bridge(${IMPORT(${CHANNELS(${seize_target})},BRIDGEPEER)})
exten => _X.,n,Gosub(macro-hangupcall,s,1())
@therestlesstexan
Copy link

Yeah, I haven't got a clue either LOL @lgaetz - the next day, with ZERO interaction from me, it just started working using GoSub on my 17 box. Go figure. LOL - Thanks regardless, tho!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment