Push notifications for Ring Groups with Linphone

ArthurJST

New Member
Oct 30, 2025
2
0
1
26
Hello,

I'm in the process of implementing push notification services for FusionPBX using Linphone
I've got a script working that is calling their API to wake up the phone and then the PBX sends the invite to the app
It's working well for local and direct calls to the phone but it is not working for ring groups since they are not treated the same as direct calls

Does anyone have experience on how to implement modifications on the ring group dialplans/lua app to add push notifications ?

I'm adding the custom dialplan that I made for Push notifications on local calls and the script that I used (coded by AI sorry) to send the request to Linphone

I would really appreciate the help, and maybe it would help some more people if we can turn this into some form of documentation later

Here is the custom dialplan that I use for extensions that need push notifications

XML:
<extension name="local_push_extension" continue="true" uuid="c8b85a8a-af88-474c-8848-9756fbf88417">
    <condition field="${user_exists}" expression="true"/>
    <condition field="${user_data ${destination_number}@${domain_name} var push_enabled}" expression="true">
        <action application="set" data="push_enabled=${user_data ${destination_number}@${domain_name} var push_enabled}"/>
        <action application="export" data="dialed_extension=${destination_number}" inline="true"/>
        <action application="limit" data="hash ${domain_name} ${destination_number} ${limit_max} ${limit_destination}" inline="false"/>
        <action application="log" data="Dialplan de push utilisé"/>
    </condition>
    <condition field="" expression="">
        <action application="set" data="hangup_after_bridge=true"/>
        <action application="set" data="continue_on_fail=true"/>
        <action application="set" data="initial_callee_id_name=${user_data(${dialed_extension}@${domain_name} var effective_caller_id_name)}"/>
        <action application="hash" data="insert/${domain_name}-call_return/${dialed_extension}/${caller_id_number}"/>
        <action application="hash" data="insert/${domain_name}-last_dial_ext/${dialed_extension}/${uuid}"/>
        <action application="set" data="called_party_call_group=${user_data(${dialed_extension}@${domain_name} var call_group)}"/>
        <action application="hash" data="insert/${domain_name}-last_dial/${called_party_call_group}/${uuid}"/>
        <action application="set" data="api_hangup_hook=lua app.lua hangup"/>
        <action application="export" data="domain_name=${domain_name}"/>
        <action application="lua" data="linphone_push_bg.lua"/>
        <action application="sleep" data="4000"/>
        <action application="bridge" data="{sip_invite_call_id=${sip_call_id}}user/${destination_number}@${domain_name}"/>
        <action application="lua" data="app.lua failure_handler"/>
    </condition>
</extension>

And here is the lua script that is being called in the dialplan :

Code:
local DOMAIN    = session:getVariable("domain_name")
local EXTENSION = session:getVariable("destination_number")
local CALL_ID   = session:getVariable("sip_call_id")
local push_enabled = session:getVariable("push_enabled")


if DOMAIN ~= "MYDOMAIN.COM" then return end

local API_KEY  = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
local PUSH_URL = "https://XXXXXXXXXXXXXXXXXXXXXXXXX/api/push_notification"

local sofia_api = freeswitch.API()
local contact   = sofia_api:execute("sofia_contact", "internal/" .. EXTENSION .. "@" .. DOMAIN)

freeswitch.consoleLog("INFO", "[LinphonePushBG] contact=" .. tostring(contact) .. "\n")

local pn_provider = contact and contact:match("pn%-provider=([^;>%s,]+)")
local pn_prid_raw = contact and contact:match("pn%-prid=([^;>%s,]+)")

if not pn_provider or not pn_prid_raw then
    freeswitch.consoleLog("WARNING", "[LinphonePushBG] Pas de paramètres push\n")
    return
end

local pn_param, pn_prid, data

if pn_provider == "apns" then
    pn_param = "XXXXXXXXXXXXXXXXXX"
    pn_prid  = pn_prid_raw:match("([^:]+)")
    data = string.format(
        '{"pn_provider": "apns", "pn_param": "%s", "pn_prid": "%s", "type": "call", "call_id": "%s"}',
        pn_param, pn_prid, CALL_ID
    )
elseif pn_provider == "fcm" then
    pn_param = "XXXXXXXXXXXXXXX"
    pn_prid  = pn_prid_raw
    data = string.format(
        '{"pn_provider": "fcm", "pn_param": "%s", "pn_prid": "%s", "type": "call", "call_id": "%s"}',
        pn_param, pn_prid, CALL_ID
    )
else
    return
end

freeswitch.consoleLog("INFO", "[LinphonePushBG] JSON envoyé: " .. data .. "\n")

local cmd = "curl -v -k --location --request POST '" .. PUSH_URL .. "'"
         .. " --header 'Content-Type: application/json'"
         .. " --header 'Accept: application/json'"
         .. " --header 'x-api-key: " .. API_KEY .. "'"
         .. " --data '" .. data .. "'"
         .. " > /tmp/push_result_" .. EXTENSION .. ".txt 2>&1 &"

os.execute(cmd)
freeswitch.consoleLog("INFO", "[LinphonePushBG] Push lancée en background\n")