Show pagesourceOld revisionsBacklinksAdd to bookExport to PDFODT exportBack to top Share via Share via... Twitter LinkedIn Facebook Pinterest Telegram WhatsApp Yammer Reddit TeamsRecent ChangesSend via e-MailPrintPermalink × Book Creator Add this page to your book Book Creator Remove this page from your book Manage book(0 page(s)) Help teams_all.xml <?xml version="1.0" encoding="utf-8"?><configuration patchDefault="true"> <luaAutoStarters> <luaScript name="Automatic Token Request"> <code><![CDATA[ -- Variables that depend on Microsofts API local refreshUrl = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token" local content = "client_id=[Client-ID]&scope=presence.read&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient&grant_type=refresh_token&refresh_token=" -- Configure the timing. Tokens last for 3599 seconds so the overall refresh-time must be lower. local refreshInterval = 20 -- check every 20 seconds if there is something new to do local refreshCountdownMax = 150 -- once authentication was successful, wait 150 times 20 seconds until refresh local refreshCountdown = 0 -- current live-ticker decremented once every 20 seconds, new credentials are fetched when it reaches 0 -- Analyzes the response from Microsoft to the request of (new) tokens local function analyzeResponse(responseCode, responseBody, responseHeaders) if responseCode == 200 and responseBody then values = json.eval(responseBody) debug.log("Teams Automatic Token Refresh: updating tokens") persisted["msTeamsToken"] = values["access_token"] persisted["msTeamsRefresh"] = values["refresh_token"] refreshCountdown = refreshCountdownMax else debug.log("Teams Automatic Token Refresh: failed to receive tokens", "e") refreshCountdown = 0 end shared["msTeamsReAuthNeeded"] = "false" end local function refreshLoop() if persisted["msTeamsToken"] and persisted["msTeamsRefresh"] then refreshCountdown = refreshCountdown - 1 if shared["msTeamsReAuthNeeded"] and shared["msTeamsReAuthNeeded"] == "true" then debug.log("Teams Automatic Token Refresh: other scripts failed to authenticate -> trigger refresh at "..tostring(refreshCountdown), "e") refreshCountdown = 0 end if refreshCountdown <= 0 then debug.log("Teams Automatic Token Refresh: inquire token update", "d") http.post(refreshUrl, analyzeResponse, content..persisted["msTeamsRefresh"]) end else debug.log("Teams Automatic Token Refresh: not yet valid", "i") refreshCountdown = 0 end time.callbackIn(refreshLoop, refreshInterval) end -- start refreshing tokens refreshLoop() ]]></code> </luaScript> <luaScript name="Get User-Code"> <code><![CDATA[ -- Variables that depend on Microsofts API local tokenRequestUrl = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token" local tokenRequestContent ="client_id=[Client-ID]&scope=presence.read&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient&grant_type=authorization_code&code=" -- Analyze the response from Microsoft to the request of tokens local function analyzeTokenResponse(responseCode, responseBody, responseHeaders) if responseBody then debug.log("Teams Get User-Code got response: "..responseBody) end if responseCode == 200 and responseBody then values = json.eval(responseBody) debug.log("Teams Get User-Code storing access_token: "..values["access_token"]) debug.log("Teams Get User-Code storing refresh_token: "..values["refresh_token"]) persisted["msTeamsToken"] = values["access_token"] persisted["msTeamsRefresh"] = values["refresh_token"] end end -- Get a code out of the query parameters of the given URL and calls Microsofts servers to receive the tokens with the code local function analyzeRequest(requestUrl, requestBody, requestHeader, requestVariables) x1, x2 = string.find(requestVariables["query_string"], "code=") y1, y2 = string.find(requestVariables["query_string"], "&session_state") debug.log("Teams Get User-Code got request to start: "..x1.." - "..x2.." - "..y1.." - "..y2) if x1 and y2 then code = string.sub(requestVariables["query_string"], x2+1, y1-1) http.post(tokenRequestUrl,analyzeTokenResponse,tokenRequestContent..code) end end -- Listen to the address <Phone-IP>/api/v1/exec/getTeamsCode and gives the request to "analyzeRequest" http.listen("getTeamsCode", analyzeRequest, true)]]></code> </luaScript> </luaAutoStarters> <templates> <template name="@string/speed_dial_title" prio="30" icon="@drawable/invite"> <keyConfiguration> <lua> <code><![CDATA[local gUserpath = "/identities/identity["..pIdentity.."]/" local gSubscriptionIsTrying = false local gSubscriptionIsWorking = false local gUser = "" -- username of associated identity local gDomain = "" -- domain (registrar) of associated identity local gPickupCode = nil -- pickup-code of associated identity local gDoPickupWithReplaces = false -- whether or not to use a replaces header (instead of pickup-code) to pick a call local gRemoteUri = nil -- sipUri-object of remote local gRemoteUser = nil -- user-part of remote uri local gOngoingSubscription = nil local gWithSubscription = pSubscriptionEnabled=="true" local gWithPickup = pDoPickup=="true" local gWithMissed = pBlinkOnMissed=="true" local gWithAutoAnswer = pRequestAutoAnswer=="true" local gHaveMissedCall = false -- remember what we're signaling to the user, this will influence what is expected when key is pressed -- one of: -- - errors-state: error, trying -- - subscription-event: idle, pickable, busy -- - local call: calling, active, holding, ringing -- - missed call: missed -- - nothing going on: idle local gLastReportedState = nil local gLocalCallId = nil -- wenn we have local call with gRemoteUser -> store its call-id here (e.g. TC@4) local gLocalCallState = nil -- wenn we have local call with gRemoteUser -> store its state here (calling, active, holding or ringing) local gFctName = "Blf" -- name for this key in logs (uri and identity will be added during initialization) -- keep infos of all monitored calls on our remote, only used when gWithSubscription==true -- contains these entries: -- - "entity" : attribute from dialog-info-xml -- - "version" : attribute from dialog-info-xml -- - "metaState" : one of free/pickable/busy -> combined meta-state accumulated from all dialogs (monitored calls) -- - "activeDialogs" : array of all dialog-ids of the all dialogs responsible for signaled meta-state. -- - "dialogs" : map by dialog-id of all dialogs (monitored calls) local gDialogInfo = nil local gTeamsFailedIdAquire = false local gTeamsReceivedServerResponse = false local gTeamsServerResponseWasGood = false local gTeamsServerResponse = 42 local gTeamsState = "Unknown" -- one of: Available, Away, BeRightBack, Busy, DoNotDisturb, Offline, Unknown local gTeamsRefreshInterval = 17 -- poll for new state every xy seconds local gTeamsId = nil local gTeamsPresenceUrl = "https://graph.microsoft.com/beta/communications/presences/" local gTeamsFetchIdUrl = "https://graph.microsoft.com/beta/users/" local function isValid() -- reports if key has all it's params set to valid values. Set to nil to keep this default if pIdentity == "" or pIdentity == nil then return false end if pRemote == "" or pRemote == nil then return false end return true end -- Create value for replaces-header for first pickable dialog local function getFirstReplacesInfo() if #gDialogInfo.activeDialogs == 0 or not gDialogInfo.metaState == "pickable" then debug.log(gFctName..": cannot find pickable call for replaces", "e") return nil end local dlgXml = gDialogInfo.dialogs[gDialogInfo.activeDialogs[1]] local callId = dlgXml["call-id"] if not callId or string.len(callId) == 0 then debug.log(gFctName..": cannot find call-id for replaces-header", "e") return nil end return sipTools.mkReplaces{callId=callId, to=dlgXml["remote-tag"], from=dlgXml["local-tag"]} end -- analyze existing dialogs and find a text that describes who our monitored remote is talking to local function getBlfRemoteText() if #gDialogInfo.activeDialogs == 0 then return nil elseif #gDialogInfo.activeDialogs > 1 and not gDoPickupWithReplaces then -- wouldn't know which one would be picked return "multiple calls" end -- find something to name the pick-party: local remote = gDialogInfo.dialogs[gDialogInfo.activeDialogs[1]]:find("remote") if not remote then debug.log(gFctName..": missing <remote> within <dialog-info>, cannot name remote party", "w") return "@string/call_unknown" end local dispName = remote:find("identity", "display") if dispName and string.len(dispName.display) > 0 then return dispName.display end local remote_usr = remote:find("identity") if not remote_usr then remote_usr = remote:find("target") end if not remote_usr or not remote_usr[1] then debug.log(gFctName..": missing remote-uri within <dialog-info>, cannot name remote party", "w") return "@string/call_unknown" end if string.find(string.lower(remote_usr[1]), "anonymous") then return "@string/call_anonyme" end local remote_uri = sipTools.parseUri{sipUri=remote_usr[1]} if not remote_uri or not remote_uri:getUiNumber() then return remote_usr[1] end return remote_uri:getUiNumber() end local function updateKey() local desiredInfo = " " local desiredIcon = nil if not isValid() then gLastReportedState = "error" key:setLed("orange", false) desiredInfo = "setup failed" desiredIcon = "@drawable/block" else gLastReportedState = "idle" if gLocalCallState == "calling" then gLastReportedState = "calling" key:setLed("green", false) desiredInfo = "calling" elseif gLocalCallState == "ringing" then gLastReportedState = "ringing" key:setLed("green", true) desiredInfo = "ringing" elseif gLocalCallState == "holding" then gLastReportedState = "holding" key:setLed("green", true) desiredInfo = "holding" elseif gLocalCallState == "active" then gLastReportedState = "active" key:setLed("green", false) desiredInfo = "active" elseif gWithSubscription then if gSubscriptionIsWorking then if gDialogInfo.metaState == "busy" then gLastReportedState = "busy" key:setLed("red", false) desiredInfo = getBlfRemoteText() elseif gDialogInfo.metaState == "pickable" then gLastReportedState = "pickable" key:setLed("red", true) desiredIcon = "@drawable/pick_up" desiredInfo = getBlfRemoteText() else gLastReportedState = "idle" key:setLed("off") end elseif gSubscriptionIsTrying then gLastReportedState = "trying" key:setLed("orange", true) desiredInfo = "trying to subscribe" else gLastReportedState = "error" key:setLed("orange", false) desiredInfo = "failed to subscribe" end else key:setLed("off") end if gLastReportedState == "idle" and gTeamsState ~= "Unknown" then desiredInfo = gTeamsState if gTeamsState == "DoNotDisturb" or gTeamsState == "Busy" then gLastReportedState = "busy" key:setLed("red") elseif gTeamsState == "Available" then gLastReportedState = "idle" key:setLed("green") else -- Away, BeRightBack, Offline gLastReportedState = "idle" key:setLed("off") end end if gLastReportedState == "idle" and gHaveMissedCall then gLastReportedState = "missed" key:setLed("green", true) desiredInfo = "missed call" end end key:setInfo(desiredInfo) key:setIcon(desiredIcon) end function onDbResult(entries) gHaveMissedCall = false for i, queryParam in ipairs(entries) do local entryUser = sipTools.parseUri{sipUri=queryParam.number}:getUser() if entryUser == gRemoteUser then debug.log(gFctName..": found missed call from "..tostring(queryParam.number), "d") gHaveMissedCall = true break end end updateKey() end function onCallEvent(event, call1, call2) if event == nil or call1 == nil then debug.log(gFctName..": invalid parameters at callListChanged("..tostring(event).." ,"..tostring(call1)..")", "e") return end local callId = call1:getId() local state = call1:getState() local number, name = call1:getRemote() local regId = tostring(call1:getIdentityIndex()) if regId ~= pIdentity then return end debug.log("received "..tostring(event).." for call to "..tostring(number)..", id="..tostring(regId)..", state="..tostring(state)..", callId="..tostring(callId), "v") local remoteUri = sipTools.parseUri{sipUri=number} local remoteUser = remoteUri:getUser() if remoteUser ~= gRemoteUser then if callId == gLocalCallId then debug.log("call-partner of local "..callId.." changed, call no longer belongs to "..gFctName, "d") gLocalCallId = nil gLocalCallState = nil updateKey() end return end if state == "calling" or state == "ringing" or state == "active" or state == "holding" then if gLocalCallId ~= callId then debug.log(gFctName.." found new local call "..callId.." in state "..state, "d") elseif gLocalCallState ~= state then debug.log(gFctName.." "..callId.." changed state to "..state, "d") end gLocalCallId = callId gLocalCallState = state else debug.log(gFctName.." "..callId.." ended", "d") gLocalCallId = nil gLocalCallState = nil end updateKey() end local function createDialogInfoStruct(dlgVers, dlgEntity) local result = {} result.entity = dlgEntity result.version = dlgVers result.metaState = "free" result.activeDialogs = {} result.dialogs = {} return result end local function handleNotify(subscr, data, headers) gSubscriptionIsWorking = true gSubscriptionIsTrying = false local dlgInfo = xml.eval(data) if dlgInfo ~= nil then debug.log(gFctName.." received Notify", "d") if dlgInfo:tag() ~= "dialog-info" then debug.log(gFctName..": did not receive a dialog-info, ignoring "..tostring(dlgInfo:tag()) , "e") return end local dlgVers = dlgInfo.version local dlgEntity = dlgInfo.entity local dlgState = dlgInfo.state if not dlgEntity or not dlgVers or not dlgState then debug.log(gFctName..": received dialog-info is missing essencial attributes, ignoring dialog-info with " ..tostring(dlgVers).." / "..tostring(dlgEntity).." / "..tostring(dlgState) , "e") return end if not gDialogInfo then -- assuming this is the first dialog-info we've received -> initialize gDialogInfo gDialogInfo = createDialogInfoStruct(dlgVers, dlgEntity) else if gDialogInfo.entity ~= dlgEntity then -- strange -> begin again debug.log(gFctName..": entity in dialog-info changed, re-initializing gDialogInfo", "w") gDialogInfo = createDialogInfoStruct(dlgVers, dlgEntity) elseif gDialogInfo.version >= dlgVers then debug.log(gFctName..": received outdated dialog-info -> ignoring version "..tostring(dlgVers), "i") end end if dlgState == "full" then gDialogInfo.dialogs = {} end for _,dlg in ipairs(dlgInfo) do local dlgId = dlg.id if dlgId and string.len(dlgId) > 0 then gDialogInfo.dialogs[dlgId] = dlg end end -- evaluate metaState: local busyDlgs = {} local pickableDlgs = {} for dlgId, dlg in pairs(gDialogInfo.dialogs) do local stateXml = dlg:find("state") local state = nil if stateXml and #stateXml >= 1 then state = stateXml[1] end if state == "confirmed" then busyDlgs[#busyDlgs+1] = dlgId elseif state == "terminated" then -- ignore else -- assuming proceeding, early oder trying local direction = dlg.direction if direction == "initiator" then busyDlgs[#busyDlgs+1] = dlgId else pickableDlgs[#pickableDlgs+1] = dlgId end end end if #pickableDlgs > 0 then gDialogInfo.activeDialogs = pickableDlgs gDialogInfo.metaState = "pickable" elseif #busyDlgs > 0 then gDialogInfo.activeDialogs = busyDlgs gDialogInfo.metaState = "busy" else gDialogInfo.activeDialogs = {} gDialogInfo.metaState = "free" end debug.log(gFctName..": state from Notify: "..gDialogInfo.metaState, "d") else debug.log(gFctName..": received empty notify from "..subscr:getUri() , "e") end updateKey() end local function unsubscribe() gSubscriptionIsWorking = false gSubscriptionIsTrying = false if (gOngoingSubscription) then gOngoingSubscription:unsubscribe() gOngoingSubscription = nil end end local function subscriptionTerminated() gSubscriptionIsWorking = false gSubscriptionIsTrying = false updateKey() end local function onSubscrIsTrying() gSubscriptionIsWorking = false gSubscriptionIsTrying = true updateKey() end local function subscribe() unsubscribe() gOngoingSubscription = sip.subscribe{uri=pRemote, onNotify=handleNotify, line=pIdentity, onTerminated=subscriptionTerminated, onTrying=onSubscrIsTrying} end local function identityChanged(path) gPickupCode = config.get(gUserpath .. "pickupCode") gDoPickupWithReplaces = not gPickupCode or string.len(gPickupCode) == 0 local new_user = config.get(gUserpath .. "username") local new_domain = sip.identities.getDomain(pIdentity); if new_user ~= gUser or new_domain ~= domain then unsubscribe() gUser = new_user gDomain = new_domain subscribe() updateKey() end end local function setupRegChange() config.register(gUserpath, identityChanged) identityChanged() end function onKeyUp() local headers = {} local doPickup = false debug.log(gFctName.."-key pressed", "d") if not isValid() then debug.log("missing parameter(s), "..gFctName.."-key won't work ever", "e") return elseif gLastReportedState == "error" then debug.log(gFctName..": subscription had failed before, attempting restart", "d") subscribe() updateKey() return elseif gLastReportedState == "trying" then debug.log(gFctName..": subscription is being tried: stopping and restarting", "d") unsubscribe() subscribe() updateKey() return elseif gLastReportedState == "calling" then debug.log(gFctName..": outgoing call thats not connected yet -> ignore key-press", "d") return elseif gLastReportedState == "active" then debug.log(gFctName..": connected call -> hold", "d") sip.calls.hold(gLocalCallId) return elseif gLastReportedState == "holding" then debug.log(gFctName..": held call -> retrieve", "d") sip.calls.resume(gLocalCallId) return elseif gLastReportedState == "ringing" then debug.log(gFctName..": ringing call -> accept", "d") sip.calls.accept(gLocalCallId) return elseif gLastReportedState == "pickable" then debug.log(gFctName..": picking the call", "d") local replHdr, replVal = getFirstReplacesInfo() if replHdr then -- always add replace, even with pickup-code .. cant hurt headers[replHdr] = replVal end if not gDoPickupWithReplaces then debug.log(gFctName..": picking with pickup-code '"..gPickupCode.."'", "d") doPickup = true else if replHdr then debug.log(gFctName..": picking with Replaces-Header: "..replVal, "d") else debug.log(gFctName..": cannot pick, replaces-info unavailable", "e") return end end else debug.log(gFctName..": simply calling the remote (state: "..gLastReportedState..", autoAnswer: "..tostring(gWithAutoAnswer)..")", "d") if gWithAutoAnswer then headers['Alert-Info'] = '<http://192.168.182.156>;info=alert-autoanswer' end end sip.invite{uri=pRemote, line=pIdentity, hidden=false, headers=headers, pickup=doPickup} end --Analyzes the response of Microsoft and check if the user in question is available local function onTeamsPresenceStatus(responseCode, responseBody, responseHeaders) gTeamsReceivedServerResponse = true gTeamsServerResponseWasGood = responseCode == 200 and responseBody gTeamsServerResponse = responseCode if gTeamsServerResponseWasGood then values = json.eval(responseBody) if values["availability"] == "AvailableIdle" then gTeamsState = "Available" elseif values["availability"] == "PresenceUnknown" then gTeamsState = "Unknown" elseif values["availability"] == "BusyIdle" then gTeamsState = "Busy" else gTeamsState = values["availability"] end else gTeamsState = "Unknown" if responseCode == 401 then shared["msTeamsReAuthNeeded"] = "true" end end updateKey() end --Requests the teams status of someone local function requestTeamsPresenceStatus() gTeamsReceivedServerResponse = false local header = {} header["Authorization"] = "Bearer "..persisted["msTeamsToken"] http.get(gTeamsPresenceUrl..gTeamsId, onTeamsPresenceStatus, header) end -- read persons ID from response local function onTeamsIdResponse(responseCode, responseBody, responseHeaders) gTeamsServerResponse = responseCode if responseCode== 200 and responseBody then values = json.eval(responseBody) gTeamsId = values["id"] debug.log(gFctName..", Teams: got id"..gTeamsId, "v") requestTeamsPresenceStatus() else debug.log(gFctName..", Teams: failed to inquire id", "d") gTeamsFailedIdAquire = true if responseCode == 401 then shared["msTeamsReAuthNeeded"] = "true" end end updateKey() end -- if pTeamsId is an e-mail -> requests basic data from Microsoft to get the ID local function fetchTeamsId() x,y = string.find(pTeamsId, "@", 1, true) if x ~= nil then local header = {} header["Authorization"] = "Bearer "..persisted["msTeamsToken"] http.get(gTeamsFetchIdUrl..pTeamsId, onTeamsIdResponse, header) else gTeamsId = pTeamsId debug.log(gFctName..", Teams: got id"..gTeamsId, "v") requestTeamsPresenceStatus() end end local function teamsRefreshLoop() if not persisted["msTeamsToken"] then debug.log(gFctName..", Teams: not ready yet", "d") elseif not pTeamsId then debug.log(gFctName..", Teams: disabled", "d") return elseif not gTeamsId then debug.log(gFctName..", Teams: fetch id", "v") fetchTeamsId() else requestTeamsPresenceStatus() end time.callbackIn(teamsRefreshLoop, gTeamsRefreshInterval) end local function initialize() if isValid() then setupRegChange() else debug.log("missing parameters, "..gFctName.."-key won't work", "e") updateKey() return end gRemoteUri = sipTools.parseUri{sipUri=pRemote, fromUserInput=true, idIdx=pIdentity} gRemoteUser = gRemoteUri:getUser() local dbQuery = "(is_read=0 OR is_read is NULL ) AND new=1 AND subscription_id="..pIdentity.." AND number LIKE '%"..gRemoteUser.."%' AND (type=3 OR type=5)" database.subscribe{dbUri="content://call_log/calls", onChanged=onDbResult, query=dbQuery, columns={"number"}} sip.calls.listen(onCallEvent) gFctName = "BLF["..gRemoteUri:getUiNumber().." on id "..pIdentity.."]" teamsRefreshLoop() updateKey() end initialize()]]></code> <params> <param name="pRemote"/> <param name="pIdentity"/> <param name="pSubscriptionEnabled"><value>true</value></param> <param name="pDoPickup"><value>true</value></param> <param name="pRequestAutoAnswer"><value>false</value></param> <param name="pBlinkOnMissed"><value>true</value></param> <param name="pTeamsId"/> </params> </lua> </keyConfiguration> <parameters> <parameter name="@string/uri" type="text"> <path>//param[@name="pRemote"]/value</path> </parameter> <parameter optional="true" name="@string/identity" type="identity"> <path>//param[@name="pIdentity"]/value</path> </parameter> <parameter optional="true" name="@string/subscription_enabled" type="boolean"> <path>//param[@name="pSubscriptionEnabled"]/value</path> </parameter> <parameter optional="true" name="@string/do_pickup" type="boolean"> <path>//param[@name="pDoPickup"]/value</path> </parameter> <parameter optional="true" name="@string/auto_answer" type="boolean"> <path>//param[@name="pRequestAutoAnswer"]/value</path> </parameter> <parameter optional="true" name="@string/with_calllog" type="boolean"> <path>//param[@name="pBlinkOnMissed"]/value</path> </parameter> <parameter optional="true" name="E-Mail" type="text"> <path>//param[@name="pTeamsId"]/value</path> </parameter> </parameters> </template> <template name="Teams Presence"> <keyConfiguration> <lua> <code><![CDATA[ local gTeamsFailedIdAquire = false local gTeamsReceivedServerResponse = false local gTeamsServerResponseWasGood = false local gTeamsServerResponse = 42 local gTeamsState = "Unknown" -- one of: Available, Away, BeRightBack, Busy, DoNotDisturb, Offline, Unknown local gTeamsRefreshInterval = 17 -- poll for new state every xy seconds local gTeamsId = nil local gTeamsPresenceUrl = "https://graph.microsoft.com/beta/communications/presences/" local gTeamsFetchIdUrl = "https://graph.microsoft.com/beta/users/" --Updates the led depending on the Status local function updateKey() if gTeamsFailedIdAquire then key:setLed("orange") key:setInfo("error getting ID: "..tostring(gTeamsServerResponse)) elseif not persisted["msTeamsToken"] then key:setLed("orange") key:setInfo("refresh failure") elseif not gTeamsReceivedServerResponse then key:setLed("orange", true) key:setInfo("aquiring") elseif not gTeamsServerResponseWasGood then key:setLed("orange") key:setInfo("server response: "..tostring(gTeamsServerResponse)) elseif gTeamsState == "Available" then key:setLed("green") key:setInfo(gTeamsState) else key:setLed("red") key:setInfo(gTeamsState) end end --Analyzes the response of Microsoft and check if the user in question is available local function onTeamsPresenceStatus(responseCode, responseBody, responseHeaders) gTeamsReceivedServerResponse = true gTeamsServerResponseWasGood = responseCode == 200 and responseBody gTeamsServerResponse = responseCode if gTeamsServerResponseWasGood then values = json.eval(responseBody) debug.log("Teams Presence: current activity is "..values["activity"].." in state "..values["availability"]) if values["availability"] == "AvailableIdle" then gTeamsState = "Available" elseif values["availability"] == "PresenceUnknown" then gTeamsState = "Unknown" elseif values["availability"] == "BusyIdle" then gTeamsState = "Busy" else gTeamsState = values["availability"] end else gTeamsState = "Unknown" if responseCode == 401 then debug.log("Teams Presence: triggering re-auth") shared["msTeamsReAuthNeeded"] = "true" end end updateKey() end --Requests the teams status of someone local function requestTeamsPresenceStatus() gTeamsReceivedServerResponse = false debug.log("Teams Presence: requesting state", "d") local header = {} header["Authorization"] = "Bearer "..persisted["msTeamsToken"] http.get(gTeamsPresenceUrl..gTeamsId, onTeamsPresenceStatus, header) end -- read persons ID from response local function onTeamsIdResponse(responseCode, responseBody, responseHeaders) gTeamsServerResponse = responseCode if responseCode== 200 and responseBody then values = json.eval(responseBody) gTeamsId = values["id"] debug.log("Teams Presence: got id"..gTeamsId, "d") requestTeamsPresenceStatus() else gTeamsFailedIdAquire = true if responseCode == 401 then debug.log("Teams Presence: triggering re-auth") shared["msTeamsReAuthNeeded"] = "true" end end updateKey() end -- if pTeamsId is an e-mail -> requests basic data from Microsoft to get the ID local function fetchTeamsId() x,y = string.find(pTeamsId, "@", 1, true) if x ~= nil then local header = {} header["Authorization"] = "Bearer "..persisted["msTeamsToken"] http.get(gTeamsFetchIdUrl..pTeamsId, onTeamsIdResponse, header) else gTeamsId = pTeamsId debug.log("Teams Presence: got id"..gTeamsId, "d") requestTeamsPresenceStatus() end end local function teamsRefreshLoop() if not persisted["msTeamsToken"] then debug.log("Teams Presence: not ready yet", "d") elseif not pTeamsId then debug.log("Teams Presence: disabled", "d") return elseif not gTeamsId then debug.log("Teams Presence: fetch id", "d") fetchTeamsId() else requestTeamsPresenceStatus() end time.callbackIn(teamsRefreshLoop, gTeamsRefreshInterval) end function onKeyUp() -- think of something to do end teamsRefreshLoop() updateKey() ]]></code> <params> <param name="pTeamsId"/> </params> </lua> </keyConfiguration> <parameters> <parameter name="E-Mail"> <path>//param[@name="pTeamsId"]/value</path> </parameter> </parameters> </template> <template name="Teams dnd toggle"> <keyConfiguration> <lua> <code><![CDATA[ local gTeamsFailedIdAquire = false local gTeamsReceivedServerResponse = false local gTeamsServerResponseWasGood = false local gTeamsServerResponse = 42 local gTeamsState = "Unknown" -- one of: Available, Away, BeRightBack, Busy, DoNotDisturb, Offline, Unknown local gTeamsRefreshInterval = 7 -- poll for new state every xy seconds local gTeamsId = nil local gTeamsPresenceUrl = "https://graph.microsoft.com/beta/communications/presences/" local gTeamsFetchIdUrl = "https://graph.microsoft.com/beta/users/" -- variables needed to activate "Do not Disturb" local gDndOn = "##8*211" local gDndOff = "##8*210" local gUserpath = "/identities/identity["..pIdentity.."]/" local gUser = config.get(gUserpath .. "username") --Updates the led depending on the Status local function updateKey() if gTeamsFailedIdAquire then key:setLed("orange") key:setInfo("error getting ID: "..tostring(gTeamsServerResponse)) elseif not persisted["msTeamsToken"] then key:setLed("orange") key:setInfo("refresh failure") elseif not gTeamsReceivedServerResponse then key:setLed("orange", true) key:setInfo("aquiring") elseif not gTeamsServerResponseWasGood then key:setLed("orange") key:setInfo("server response: "..tostring(gTeamsServerResponse)) elseif gTeamsState == "Available" then key:setLed("green") key:setInfo(gTeamsState) else key:setLed("red") key:setInfo(gTeamsState) end end --Analyzes the response of Microsoft and check if the user in question is available local function onTeamsPresenceStatus(responseCode, responseBody, responseHeaders) gTeamsReceivedServerResponse = true gTeamsServerResponseWasGood = responseCode == 200 and responseBody gTeamsServerResponse = responseCode if gTeamsServerResponseWasGood then values = json.eval(responseBody) debug.log("Teams Presence: current activity is "..values["activity"].." in state "..values["availability"]) local newTeamsState = values["availability"] if values["availability"] == "AvailableIdle" then newTeamsState = "Available" elseif values["availability"] == "PresenceUnknown" then newTeamsState = "Unknown" elseif values["availability"] == "BusyIdle" then newTeamsState = "Busy" end if newTeamsState ~= gTeamsState then if newTeamsState == "Available" then sip.invite{uri=gDndOff.."#"..gUser, line=pIdentity, hidden=true} elseif gTeamsState == "Available" then sip.invite{uri=gDndOn.."#"..gUser, line=pIdentity, hidden=true} end gTeamsState = newTeamsState end else gTeamsState = "Unknown" if responseCode == 401 then debug.log("Teams Presence: triggering re-auth") shared["msTeamsReAuthNeeded"] = "true" end end updateKey() end --Requests the teams status of someone local function requestTeamsPresenceStatus() gTeamsReceivedServerResponse = false debug.log("Teams Presence: requesting state", "d") local header = {} header["Authorization"] = "Bearer "..persisted["msTeamsToken"] http.get(gTeamsPresenceUrl..gTeamsId, onTeamsPresenceStatus, header) end -- read persons ID from response local function onTeamsIdResponse(responseCode, responseBody, responseHeaders) gTeamsServerResponse = responseCode if responseCode== 200 and responseBody then values = json.eval(responseBody) gTeamsId = values["id"] debug.log("Teams Presence: got id"..gTeamsId, "d") requestTeamsPresenceStatus() else gTeamsFailedIdAquire = true if responseCode == 401 then debug.log("Teams Presence: triggering re-auth") shared["msTeamsReAuthNeeded"] = "true" end end updateKey() end -- if pTeamsId is an e-mail -> requests basic data from Microsoft to get the ID local function fetchTeamsId() x,y = string.find(pTeamsId, "@", 1, true) if x ~= nil then local header = {} header["Authorization"] = "Bearer "..persisted["msTeamsToken"] http.get(gTeamsFetchIdUrl..pTeamsId, onTeamsIdResponse, header) else gTeamsId = pTeamsId debug.log("Teams Presence: got id"..gTeamsId, "d") requestTeamsPresenceStatus() end end local function teamsRefreshLoop() if not persisted["msTeamsToken"] then debug.log("Teams Presence: not ready yet", "d") elseif not pTeamsId then debug.log("Teams Presence: disabled", "d") return elseif not gTeamsId then debug.log("Teams Presence: fetch id", "d") fetchTeamsId() else requestTeamsPresenceStatus() end time.callbackIn(teamsRefreshLoop, gTeamsRefreshInterval) end function onKeyUp() -- think of something to do end teamsRefreshLoop() updateKey() ]]></code> <params> <param name="pTeamsId"/> <param name="pIdentity"/> </params> </lua> </keyConfiguration> <parameters> <parameter name="E-Mail"> <path>//param[@name="pTeamsId"]/value</path> </parameter> <parameter name="@string/identity"> <path>//param[@name="pIdentity"]/value</path> </parameter> </parameters> </template> </templates></configuration> en/products/comfortel-d-series/developer/keys/templates/examples/teams_integration/teams_all.xml.txt Last modified: 22.03.2022 13:19by hoehne