<?xml version="1.0" encoding="utf-8"?>
<luaLibraries version="1.34.0">
<luaLibrary name="callforward">
<code><![CDATA[
local valid = false
local active = config.get( pathPrefix .. "active" )
local function updateLed()
if not valid then
key:setLed( "orange" )
elseif active == "true" then
key:setLed( "green" )
else
key:setLed( "off" )
end
end
local function checkValid()
local value = config.get( pathPrefix .. "target" )
valid = value and value:len() > 0
end
local function checkActive()
active = config.get( pathPrefix .. "active" )
end
local function configListener( path )
checkValid()
checkActive()
updateLed()
end
function onKeyUp()
--[] check for validity []--
if not valid then
return
end
local value = config.get( pathPrefix .. "active" )
if value == "false" then
config.set( pathPrefix .. "active", "true" )
elseif value == "true" then
config.set( pathPrefix .. "active", "false" )
end
end
config.register( pathPrefix, configListener )
checkValid()
updateLed()]]></code>
</luaLibrary>
<luaLibrary name="subscription_key">
<code><![CDATA[
-- returns a function that needs to be called to start this subscription_key, it takes the following parameters in this order:
local identity = "" -- index of the identity to be used
local activeColor = "green" -- which color to constantly light LED in when subscription reports 'active'
local fctName = "myKeyFct" -- name of this function to use in logs, e.g.: DND or CallForwardWhenBusy
local isValid = function() -- reports if key has all it's params set to valid values. Set to nil to keep this default
if identity == "" or identity == nil then
return false
end
return true
end
local createUri = function(onNotOff, user) -- returns the sip-uri (or at least the user-name-part) to turn the feature
return "" -- either on or off through silent call. user contains the username of the
end -- associated identity
---------------------------------------------------------------------------------------------------------------
local userpath = "/identities/identity[1]/" -- will be overwritten in start()
local subscriptionIsTrying = false
local subscriptionIsWorking = false
local activatedOnServer = false
local inLimbo = false -- true when silent call to change setting is send until new notify is received
local currentLimboTimerId = 42 -- we can only start timers, never stop -> use ID to discard obsoleted timers when they fire
local lastWasOnCmd = false -- whether last silent-call made was to activate the feature or not (aka de-activate)
local user = ""
local domain = ""
local ongoingSubscription = nil
local function updateLed()
if subscriptionIsWorking then
if activatedOnServer then
key:setLed( activeColor )
else
key:setLed( "off" )
end
elseif subscriptionIsTrying then
key:setLed("orange", true) -- trying
else
key:setLed("orange", false) -- failed, nothing can be done atm (phone will auto-retry)
end
end
local function handleNotify(subscr, data, headers)
subscriptionIsWorking = true
subscriptionIsTrying = false
local x = xml.eval(data)
if x ~= nil then
debug.log("received subscr data from "..subscr:getUri() , "i")
inLimbo = false
local set_activatedOnServer = false
for _,dlg in ipairs(x) do
state = dlg:find("state")
if state and state[1] == "trying" then
set_activatedOnServer = true
break
end
end
activatedOnServer = set_activatedOnServer
else
debug.log("received empty notify from "..subscr:getUri() , "e")
end
updateLed()
end
local function unsubscribe()
subscriptionIsWorking = false
subscriptionIsTrying = false
if (ongoingSubscription) then
ongoingSubscription:unsubscribe()
ongoingSubscription = nil
end
end
local function subscriptionTerminated()
subscriptionIsWorking = false
subscriptionIsTrying = false
updateLed()
end
local function onSubscrIsTrying()
subscriptionIsWorking = false
subscriptionIsTrying = true
updateLed()
end
local function subscribe()
unsubscribe()
ongoingSubscription = sip.subscribe{uri=createUri(true, user), onNotify=handleNotify, line=identity,
onTerminated=subscriptionTerminated, onTrying=onSubscrIsTrying}
end
local function resubscribe()
subscriptionIsWorking = false
subscriptionIsTrying = false
if (ongoingSubscription) then
ongoingSubscription:resubscribe()
else
subscribe()
end
end
local function identityChanged(path)
new_user = config.get(userpath .. "username")
new_domain = sip.identities.getDomain(identity);
if new_user ~= user or new_domain ~= domain then
unsubscribe()
user = new_user
domain = new_domain
subscribe()
updateLed()
end
end
local function setupRegChange()
config.register(userpath, identityChanged)
identityChanged()
end
local function newLimboTimer(myId)
timer = function ()
if inLimbo and myId == currentLimboTimerId then
debug.log("no news from server after we've issued a change command, trying to force update through re-subscribing", "w")
resubscribe()
updateLed()
end
end
return timer
end
local function sendCmd (turnItOnNotOff)
lastWasOnCmd = turnItOnNotOff
local url = createUri(turnItOnNotOff, user)
if url == "" or url == nil then
debug.log("there is no way to "..(turnItOnNotOff and "activate " or "deactivate ")..fctName, "e")
else
debug.log("calling server to "..(turnItOnNotOff and "activate " or "deactivate ")..fctName, "d")
if not inLimbo then
inLimbo = true
currentLimboTimerId = currentLimboTimerId + 1
time.callbackIn(newLimboTimer(currentLimboTimerId), 14)
end
sip.invite{uri=url, line=identity, hidden=true}
key:setLed(activeColor, true)
end
end
function onKeyUp()
debug.log(fctName.."-key pressed", "d")
if not isValid() then
debug.log("missing parameter(s), "..fctName.."-key won't work ever", "e")
elseif not ongoingSubscription then
debug.log("subscription had failed before, attempting restart", "i")
subscribe()
updateLed()
elseif not subscriptionIsWorking then
debug.log("subscription is being tried: stopping and restarting", "i")
resubscribe()
updateLed()
else
if inLimbo then
debug.log("no news from server yet, trying the opposite command instead", "i")
sendCmd(not lastWasOnCmd);
else
sendCmd(not activatedOnServer);
end
end
end
local startFkt = function(id, aColor, name, validFkt, cUri)
identity = id
userpath = "/identities/identity["..id.."]/"
activeColor = aColor
fctName = name
createUri = cUri
if validFkt then
isValid = validFkt
end
if isValid() then
setupRegChange()
else
debug.log("missing parameters, "..fctName.."-key won't work", "e")
end
updateLed()
end
return startFkt]]></code>
</luaLibrary>
<luaLibrary name="subscription_key_optional">
<code><![CDATA[
-- key that tries to get a subscription from PBX for feature-code ##8*26 + [relay-id] + # + [user]. If that fails,
-- it enters a compatibility-mode for old pbx'es, were key just assumes/guesses about actual state in pbx
-- returns a function that needs to be called to start this key, it takes the following 4 parameters in this order:
local identity = "1"
local relid = "900"
local fctName = "opt subscr key" -- name of this to use in logs
-- the main difference between relay- and auto_config_switch-templates was, that on key-press the former always send a
-- toggle-cmd to pbx and blinked green for 2 secs while the later always either send a on- or off-cmd and lid the led
-- either green or off:
local isCustomRelay = true
----------------------------------------------------------------------------------------------------------------v
local dtmf_sequenz = "%23%238*26"
local activeBlinkFor2secs = false
local activeColor = "green" -- which color to constantly light LED in when subscription reports 'active'
local fctName = "auto_cfg_enable at "..tostring(key.MODULE)..":"..tostring(key.INDEX) -- name of this to use in logs
local userpath = "/identities/identity["..identity.."]/"
local subscriptionIsTrying = false
local subscriptionIsWorking = false
local activatedOnServer = false
local inLimbo = false -- true when silent call to change setting is send until new notify is received
local currentLimboTimerId = 42 -- we can only start timers, never stop -> use ID to discard obsoleted timers when they fire
local lastWasOnCmd = false -- whether last silent-call made was to activate the feature or not (aka de-activate)
local user = ""
local domain = ""
local ongoingSubscription = nil
-- compatibility-mode with old pbx'es that don't know the subscription:
-- subscription will be stopped and never retried once it fails on first subscr-attempt.
-- State is reset when identity de-registers
local subscriptionWorkedOnce = false -- true when initial subsc-attempt worked
local withSubscription = true -- wether key currently uses subscription. Set to false when first subscr-attempt fails
local isValid = function() -- reports if key has all it's params set to valid values. Set to nil to keep this default
if identity == "" or identity == nil or relid == "" or relid == nil then
return false
end
return true
end
-- sip-uri to call to turn the feature on or off
local function createUri(onNotOff, user)
if isCustomRelay then -- toggle
return dtmf_sequenz..relid.."%23" --"sip:"..##8*26900#
elseif onNotOff then
return dtmf_sequenz..relid.."*1%23" --"sip:"..##8*26900*1#
else
return dtmf_sequenz..relid.."*0%23" --"sip:"..##8*26900*0#
end
end
-- the sip-uri to be used when subscribing for the state of this feature
local function createSubscrUri(user)
return dtmf_sequenz..relid.."%23"..user --"sip:"..##8*26900# + user
end
local function updateLed()
if not withSubscription then
if activeBlinkFor2secs then
activeBlinkFor2secs = false
key:setLed(activeColor, true)
time.sleep("2")
key:setLed("off")
elseif activatedOnServer then
key:setLed(activeColor)
else
key:setLed("off")
end
elseif subscriptionIsWorking then
if activatedOnServer then
key:setLed(activeColor)
else
key:setLed("off")
end
elseif subscriptionIsTrying then
key:setLed("orange", true) -- trying
else
key:setLed("orange", false) -- failed, nothing can be done atm (phone will auto-retry)
end
end
local function handleNotify(subscr, data, headers)
subscriptionIsWorking = true
subscriptionIsTrying = false
local x = xml.eval(data)
if x ~= nil then
debug.log(fctName..": received subscr data from "..subscr:getUri() , "i")
subscriptionWorkedOnce = false -- it worked once, remove marker, we know pbx supports this subscr
inLimbo = false
local set_activatedOnServer = false
for _,dlg in ipairs(x) do
state = dlg:find("state")
if state and state[1] == "trying" then
set_activatedOnServer = true
break
end
end
activatedOnServer = set_activatedOnServer
else
debug.log(fctName..": received empty notify from "..subscr:getUri() , "e")
end
updateLed()
end
local function unsubscribe()
subscriptionIsWorking = false
subscriptionIsTrying = false
if (ongoingSubscription) then
ongoingSubscription:unsubscribe()
ongoingSubscription = nil
end
end
local function subscriptionTerminated()
subscriptionIsWorking = false
subscriptionIsTrying = false
if not subscriptionWorkedOnce then
debug.log(fctName..": initial subscr-attempt failed, entering compatibility mode" , "w")
withSubscription = false
end
updateLed()
end
local function onSubscrIsTrying()
subscriptionIsWorking = false
subscriptionIsTrying = true
updateLed()
end
local function subscribe()
unsubscribe()
if withSubscription then
ongoingSubscription = sip.subscribe{uri=createSubscrUri(user), onNotify=handleNotify, line=identity,
onTerminated=subscriptionTerminated, onTrying=onSubscrIsTrying}
else
debug.log(fctName..": ignoring subscribe-request in compatibility mode", "w")
end
end
local function resubscribe()
subscriptionIsWorking = false
subscriptionIsTrying = false
if (ongoingSubscription) then
ongoingSubscription:resubscribe()
else
subscribe()
end
end
local function identityChanged(path)
new_user = config.get(userpath .. "username")
new_domain = sip.identities.getDomain(identity);
if new_user ~= user or new_domain ~= domain then
unsubscribe()
user = new_user
domain = new_domain
subscribe()
updateLed()
end
end
function onIdRegChg(newState, idIdx)
if newState == "unregistered" then
-- reset subscr-once-compatibility-mode
debug.log(fctName..": reseting compatibility mode un de-register", "i")
withSubscription = not isCustomRelay
subscriptionWorkedOnce = false
subscribe()
updateLed()
end
end
local function setupRegChange()
config.register(userpath, identityChanged)
identityChanged()
sip.identities.listen{ callback=onIdRegChg, line=identity }
end
local function newLimboTimer(myId)
timer = function ()
if inLimbo and myId == currentLimboTimerId then
debug.log(fctName..": no news from server after we've issued a change command, trying to force update through re-subscribing", "w")
resubscribe()
updateLed()
end
end
return timer
end
local function sendCmd (turnItOnNotOff)
lastWasOnCmd = turnItOnNotOff
local url = createUri(turnItOnNotOff, user)
if url == "" or url == nil then
debug.log("there is no way to "..(turnItOnNotOff and "activate " or "deactivate ")..fctName, "e")
else
debug.log("calling server to "..(turnItOnNotOff and "activate " or "deactivate ")..fctName, "d")
if withSubscription then
if not inLimbo then
inLimbo = true
currentLimboTimerId = currentLimboTimerId + 1
time.callbackIn(newLimboTimer(currentLimboTimerId), 14)
end
key:setLed(activeColor, true)
else
if isCustomRelay then
url = dtmf_sequenz..relid.."%23"
sip.invite{uri=url, line=identity, hidden=true}
updateLed()
return
end
activatedOnServer = turnItOnNotOff
updateLed()
end
sip.invite{uri=url, line=identity, hidden=true}
end
end
function onKeyUp()
debug.log(fctName.."-key pressed", "d")
if not isValid() then
debug.log("missing parameter(s), "..fctName.."-key won't work ever", "e")
elseif withSubscription then
if not ongoingSubscription then
debug.log(fctName..": subscription had failed before, attempting restart", "i")
subscribe()
updateLed()
elseif not subscriptionIsWorking then
debug.log(fctName..": subscription is being tried: stopping and restarting", "i")
resubscribe()
updateLed()
else
if inLimbo then
debug.log(fctName..": no news from server yet, trying the opposite command instead", "i")
sendCmd(not lastWasOnCmd);
else
sendCmd(not activatedOnServer);
end
end
else
if isCustomRelay then
debug.log(fctName..": in compatibility mode -> just send on-cmd and let led blink green for 2 secs", "d")
activeBlinkFor2secs = true
sendCmd(true);
updateLed();
else
debug.log(fctName..": in compatibility mode -> just send opposite of previous cmd which was active = "..tostring(activatedOnServer), "d")
sendCmd(not activatedOnServer);
updateLed();
end
end
end
local startFkt = function(id, relayId, name, isCustom)
identity = id
relid = relayId
fctName = name
isCustomRelay = isCustom
withSubscription = not isCustomRelay
userpath = "/identities/identity["..id.."]/"
if isValid() then
setupRegChange()
else
debug.log("missing parameters, "..fctName.."-key won't work", "e")
end
updateLed()
end
return startFkt]]></code>
</luaLibrary>
<luaLibrary name="local_settings_toggle_key">
<code><![CDATA[
local mDbgName = "SettingToggleKey" -- will be overwritten in start()
local mPath = "/telephony/doNotDisturb/active" -- will be overwritten in start()
local mValueA = "true" -- will be overwritten in start()
local mLedA = "green" -- will be overwritten in start()
local mValueB = "false" -- will be overwritten in start()
local mLedB = "off" -- will be overwritten in start()
local mCurrentValue = ""
function getVal(valOrFct)
if type(valOrFct) == 'function' then
return valOrFct()
else
return valOrFct
end
end
function onConfigChanged()
mCurrentValue = config.get(mPath)
local valA = getVal(mValueA)
if mCurrentValue == valA then
debug.log(mDbgName..": value changed to "..mCurrentValue.." -> setting Led to "..mLedA)
key:setLed(mLedA)
else
debug.log(mDbgName..": value changed to "..mCurrentValue.." -> setting Led to "..mLedB)
key:setLed(mLedB)
end
end
function onKeyUp()
local valA = getVal(mValueA)
local valB = getVal(mValueB)
if mCurrentValue == valA then
debug.log(mDbgName..": key pressed, setting "..mPath.." to "..valB)
config.set(mPath, valB)
else
debug.log(mDbgName..": key pressed, setting "..mPath.." to "..valA)
config.set(mPath, valA)
end
end
local start = function(name, path, valA, ledA, valB, ledB)
mPath = path
mValueA = valA
mValueB = valB
mLedA = ledA
mLedB = ledB
mDbgName = name
debug.log("register onConfigChanged")
config.register(mPath, onConfigChanged)
onConfigChanged()
end
return start
]]></code>
</luaLibrary>
<luaLibrary name="local_forward">
<code><![CDATA[
-- returns a function that needs to be called to start this local call-forwarding key,
-- it takes the following parameters in this order:
local pFwdType -- which forward settings are ment: "unconditional" or "busy" or "noResponse"?
local pTarget -- optional target where redirection should point to when activating it
----- end of start-function parameters -------------------------------------------------------------
local gPathPrefix = "/telephony/callForwarding/unconditional/"
local gLabel = nil -- label set for the key
local gActive = false
local gValid = false -- updated in update()
local gDesiredTarget -- updated in update()
local gCurrentTarget -- updated in update()
local function getDesiredTarget()
if pTarget and pTarget:len() > 0 then
return pTarget
end
return config.get(gPathPrefix .. "target")
end
local function getKeySettingsPath()
if key and key.INDEX and key.MODULE then
return "keys/"..key.MODULE.."/key[@keyNumber=\""..key.INDEX.."\"]"
end
return nil
end
local function update()
gDesiredTarget = getDesiredTarget()
gCurrentTarget = config.get(gPathPrefix .. "target")
local newValid = true
local newInfo = nil
if not gLabel or gLabel ~= gDesiredTarget then
newInfo = gDesiredTarget -- only name target when label doesn't already
end
if gActive then
if gCurrentTarget and gCurrentTarget:len() > 0 then
if gCurrentTarget == gDesiredTarget then
key:setLed("green") -- active with selected desired target: green like we always did
else
key:setLed("red") -- active but forwarding somewhere else: red as alternative on-color
end
else
key:setLed("orange") -- active without a target cannot work: signal error
newInfo = "@string/missing_target"
newValid = false
end
else
if gDesiredTarget and gDesiredTarget:len() > 0 then
key:setLed("off") -- normal off: no forwarding
else
key:setLed("orange") -- no target when one would try to press the key: signal error
newInfo = "@string/missing_target"
newValid = false
end
end
key:setInfo(newInfo)
gValid = newValid
end
local function checkActive()
gActive = config.get(gPathPrefix .. "active") == "true"
end
local function configListener(path)
checkActive()
update()
end
function onKeyUp()
if not gValid then
return
end
if gActive then
if gCurrentTarget == gDesiredTarget then
config.set(gPathPrefix .. "active", "false")
else
-- active but pointing to wrong target -> fix it
config.set(gPathPrefix .. "target", gDesiredTarget)
end
else -- activate:
config.set(gPathPrefix .. "target", gDesiredTarget)
config.set(gPathPrefix .. "active", "true")
end
end
local startFkt = function(fwdType, desiredTarget)
pFwdType = fwdType
pTarget = desiredTarget
gPathPrefix = "/telephony/callForwarding/"..fwdType.."/"
local keyCfgPath = getKeySettingsPath()
if keyCfgPath then
gLabel = config.get(keyCfgPath.."/@label")
end
config.register(gPathPrefix, configListener)
update()
end
return startFkt
]]></code>
</luaLibrary>
</luaLibraries>