Call Park mit Teams

Microsoft erweiterte die Teams Telefonie-Funktionen nach und nach um weitere Funktionen Bis Anfang 2019 sollten dann auch alle Tenants die Funktion Call Park erhalten haben, die ich hier näher beschreibe.

Warum Parken?

Das Parken von Anrufen ist eine Abwandlung einer Weiterleitung oder Rückfrage. Bei einer klassischen Vermittlung kennt der angerufene Teilnehmer das nächste Ziel und kann den Anrufer schnell vermitteln. Manchmal muss der Anrufer auch etwas warten, weil der angerufene Teilnehmer erst eine Rückfrage durchführt, d.h. das Ziel des zweiten Anrufs erst kurz die Vorgeschichte des Anrufer übermittellt oder gleich die Antwort erhält, die Rückfrage selbst beendet und weiter mit dem Anrufer spricht.

Beim Parken hingegen geht es darum, den Anrufer erst mal auf die "Seite" zu legen und den Zielteilnehmer ausfindig zu machen. Der Anrufer landet also auf einem "Part-Orbit" und der Parkende bekommt passend dazu eine Platznummer. Mit der Nummer kann nun jeder auf seinem Teams Client wieder die Verbindung heranholen.

Es in etwa wie die Abgabe ihres Mantels an der Garderobe, von der Sie dann einen Nummer bekommen. Diese Nummer ist zugleich die Berechtigung den Mantel wieder abzuholen.

Das klassische Beispiel dabei sind Orte mit einer Sprechanlage und Anwendern, die gar kein persönliches Telefon haben, z.B. ein Supermarkt oder Lagerhalle. Die Telefonzentrale kann Anrufe annehmen, den Anrufer nach dem gewünschten Anliegen oder Partner fragen und dann Parken. Der ausgerufene Teilnehmer kann dann an jedes Endgerät gehen und durch Eingabe der Nummer den Anruf heranziehen und fortsetzen.

Der Orbit - und Call Park Policies

Wenn Sie an einer Garderobe ihren Mantel abgeben, wissen sie natürlich, für welche Garderobe der Zettel gilt. Wenn eine Firma sehr groß ist und viele Anrufe zu bedienen sind, kann dürfen aber die Nummern nicht ausgehen. Auch muss sichergestellt sein, dass die Garderobenummern natürlich nur für ihren Tenant gelten. Selbst innerhalb eines Tenant wäre es unschön, wenn z.B. jemand die Nummer erfährt, der gar nicht die Anrufen annehmen darf.

Daher hat Microsoft das Problem so gelöst, dass über die "Call Park Policy" bestimmt wird, welche Benutzer einen Anruf annehmen können. Für jede Call Park Policy gibt es einen eigenen Orbit. Eine Nummer kann pro Orbit nur einmal vergeben werden aber jeder Orbit ist eigenständig. Insofern können mehrere Anrufe mit der gleichen Nummer geparkt sein.

Da jeder Anwender immer nur genau eine Call Park Policy haben kann, können sie diese Kreise auch nicht überlappend einrichten. Es gibt eine 1:! Zuordnung der Benutzer zu einer Call Park Policy und damit auch zu ihrem Orbit und von wem sie geparkte Anrufe übernehmen können. Sie müssen also schon genau schaue, wann und für wen ein Parken sinnvoll ist und wann vielleicht besser eine Call Queue solche Anrufe annehmen sollte.

Zusammenhänge

Allerdings gibt es dabei einige Dinge zu beachten. Die wesentlichen Aspekte dabei sind:

  • Nur Teams Clients mit Teams Online
    Die Funktion "Parken" steht nur für Teams Clients und Teams-Telefonen zur Verfügung. Eine Nutzung für Skype for Business Online Benutzer ist nicht möglich. Damit die Anwender mit Teams "telefonieren" können, müssen Sie auch in der Betriebsart "TeamsOnly" sein, denn nur dann werden PSTN-Anrufe auch an Teams signalisiert. Die Nutzung des Island-Mode ist nicht möglich.
  • Direct Routung erfordert REFER
    Das ist erst mal nichts neues, da Microsoft sowieso nur entsprechende Gateways und SBCs zertifiziert, die mit REFER umgehen können. Allerdings müssen Sie es nun auch korrekt konfigurieren. Es reicht nicht einfach das REFER-Verb einzuschalten sondern Sie müssen natürlich auch die Daten im "Contact"-Feld korrekt setzen und verarbeiten. REFER ist aber auch für viele andere Funktionen wie z.B. MoH erforderlich
  • Timeout für Parken: 300 Sek (nicht änderbar, Stand Okt 2019)
    Wenn ein Anwender einen Anrufer parkt und dieser nicht innerhalb von 5 Minuten wieder abgerufen wird, dann stellt Teams den Anruf wieder zum Teilnehmer durch, der das Parken veranlasst hat. Sollte der Teilnehmer nicht mehr erreichbar sein (Offline/Busy), dann verhält sich Teams wie bei einem Erstanruf und leitet den Anruf auf die Voicemail, Stellvertreter o.ä. weiter. Wenn nichts davon funktioniert, wird der Anruf beendet.
  • Nummern 10-99 (nicht änderbar, Stand Okt 2019)
    Die standardmäßig vorhandene globale Richtline reserviert die Rufnummern 10-99 für den CallPark Orbit aber ist nicht aktiv. Anders als bei Skype for Business gibt es hier allerdings keinen Konflikt. Bei SfB mussten Sie sicherstelle, dass sich Nummern nicht mit echten Rufnummern und Normalisierungsregeln in Dialplänen überlappt haben. Bei Teams werden die geparkten Anrufe über ein anderes Menü quasi "angerufen".
  • maximale geparkte Anrufe
    Über die für CallPark konfigurierten Rufnummern steuern sie auch, wie viele gleichzeitige Anrufe geparkt werden können, denn jeder Anruf beansprucht eine Rufnummer
  • Wartemusik
    In der ersten Version wird die Wartemusik von Microsoft vorgegeben. Eigene Sound-Dateien dürften irgendwann auch kommen

Für eine Aktivierung von CallPark benötigen Sie eigentlich nur noch ein Konzept welche Benutzer Sie mit welcher Richtlinie zusammenfassen. Vergessen Sie dabei aber nicht die Mitarbeiter auch entsprechend zu informieren.

Gerade das Thema "User Adoption" und "Change Management" wird bei vielen Firmen leider zu nachlässig behandelt und darf gerade bei einer Einführung von Teams nicht vergessen werden.

Konfiguration per PowerShell

Ich gehe hier davon aus, dass insbesondere mit Direct Routing die Einrichtung ihres Session Border Controllers samt REFER schon korrekt umgesetzt worden ist. Die eigentliche Konfiguration kennen Sie von vielen anderen Teams Policies vermutlich auch schon. Es gibt kein Property "CallPark" beim Benutzer sondern die CallPark-Einstellungen werden in Richtlinien zusammengefasst und dann eine davon dem Benutzer zugewiesen. So können Sie durch Änderungen an der Richtlinie das verhalten alle Benutzer anpassen, denen diese Richtlinie zugewiesen wird.

Sie müssen Sie also erst einmal mit der Skype for Business Online PowerShell verbinden um die standardmäßig vorhandene globale "Default Policy" zu sehen

PS C:\>$sfboSession = New-CsOnlineSession

PS C:\>Import-PSSession $sfboSession -allowclobber

PS C:\>Get-CsTeamsCallParkPolicy

Identity      : Global
Description   :
AllowCallPark : False

# Anfordern aller Werte
Get-CsTeamsCallParkPolicy | select *

XsAnyElements      :
XsAnyAttributes    :
Description        :
AllowCallPark      : False
PickupRangeStart   : 10
PickupRangeEnd     : 99
ParkTimeoutSeconds : 300
DataSource         : CMS
Key                : [{urn:schema:Microsoft.Rtc.Management.Policy.Teams.2017}TeamsCallParkPolicy,Tenant{xxxx},Global]
ScopeClass         : Global
Anchor             : Microsoft.Rtc.Management.ScopeFramework.GlobalScopeAnchor
Identity           : Global
TypedIdentity      : Global
Element            : <TeamsCallParkPolicy xmlns="urn:schema:Microsoft.Rtc.Management.Policy.Teams.2017"
                     AllowCallPark="false" PickupRangeStart="10" PickupRangeEnd="99" ParkTimeoutSeconds="300" />

Diese Richtlinie wirkt auf alle Personen, die keine individuelle abweichende Richtlinie habe. Microsoft beschreibt in ihrer Dokumentation, wie diese diese globale Richtlinie ändern.

Set-CsTeamsCallParkPolicy `
   -Identity Global `
   -AllowCallPark $true

#Qelle https://docs.microsoft.com/powershell/module/skype/set-csteamscallparkpolicy?view=skype-ps

Damit aktivieren sie aber CallPark für alle Benutzer mit einem gemeinsamen Orbit. Das wäre eher für kleine Firmen passend. Für einen Pilot und vermutlich auch Produktion wird man eher eine eigene Richtlinie anlege, die entweder für CallPark für die Personen erlaubt, denen im zweiten Schritt die Richtlinie zugewiesen wird. Eventuell sollten Sie aber auch mehrere Policies mit unterschiedlichen Namen aber gleichen Einstellungen Orbits anlegen, um das Heranholen von geparkten Anrufen einzuschränken. Bei der Anlage können Sie aber erst einmal nur den Namen und eine Beschreibung angeben und CallPark vorgeben.

New-CsTeamsCallParkPolicy `
   -Identity CallParkPB `
   -Description "CallPark Paderborn" `
   -AllowCallPark $true


Identity      : Tag:CallParkPB
Description   : CallPark Paderborn
AllowCallPark : True

Weitergehende Einstellungen müssen Sie dann mittels "Set-CSTeamsCallParkPolicy anpassen. Allerdings gibt es aktuell auch nur die Parameter "Description" und "AllowCallPark".

Eine so angelegte Richtlinie muss dann den Benutzer zugewiesen werden, das geht per "Grant-CSTeamsCallParkPolicy".

Grant-CsTeamsCallParkPolicy `
   -PolicyName callparkpb `
   -Identity callparktestuser@uclabor.de

Mit wenig Aufwand können Sie so auch Gruppen von Benutzern konfigurieren.

Konfiguration per Portal

Die Einstellungen lassen sich mittlerweile auch über die Webseite admin.teams.microsoft.com durchführen. Unter der URL https://admin.teams.microsoft.com/policies/callpark sehen Sie die Default Policy und deren Einstellungen:

Die Richtlinie selbst enthält folgende Einstellungen:

Sie sehen hier aber auch, dass die Werte für die Nummern und der Timeout "readonly" sind. Sie können (noch) nicht geändert werden.

Ich habe mir angewöhnt, die Default Policy nicht zu ändern und besser für die Personen, die wirklich auch CallPark nutzen, eine passende Richtlinien anzulegen.

Welche Richtlinie für einen Anwender zutrifft, können Sie im Teams Admin Portal einsehen und ändern:

In dem Beispiel wurde dem Benutzer keine Call Park-Richtlinie zugewiesen und die Einstellungen der globalen Richtlinie werden angewendet.

Sicht des Anwender

Damit fehlt nur noch die Information, was der Anwender davon letztlich mitbekommt. Zuerst heißt es auch hier einmal Geduld habe, denn die Einstellungen per PowerShell oder Teams Admin Center werden natürlich in das Teams Directory geschrieben und auch hier gibt es die ein oder andere Replikationszeit. Selbst wenn dann alle Server die Information kennen, haben auch die Teams Services einen Cache, der die Einstellungen vorhält und es hängt z.B.: davon ab, ob der Benutzer sich vor kurzem erst angemeldet oder seine Konfiguration aktualisiert hat. Rechnen Sie mal mit 1-2 Stunden.

Aber selbst dann merkt der Anwender nicht sofort etwas, denn auch der Client muss die neue Konfiguration laden. Das macht er bei der Anmeldung und natürlich auch ab und an zwischendurch. Das Thema kennen Sie aber auch schon aus der Skype for Business-Zeit.

Sobald die Einstellungen beim Client angekommen sind und ein Anruf aktiv sind, kann der Anwender den Anruf auch parken.

Teams teilt dem Anwender die Parknummer mit, die der Anwender dann über einen beliebigen Weg an den gewünschten Empfänger übermittelt.

Achtung: Wenn der Anwender hier das "X" drückt, wird der Anruf nicht beendet sondern bleibt geparkt aber der Park-Code ist nicht mehr in Erfahrung zu bringen!

Einen Anruf kann ein Anwender am einfachsten dadurch abholen, dass er sich unter den Kurzwahlen auf "geparkte Anrufe" geht und dort die Nummer eingibt.

Selbst hier zeigt der Teams-Client keine Liste der aktuell geparkten Anrufe an, sondern sie müssen die Nummer schon wissen. 

Das funktioniert natürlich auch auf einem Teams-Telefon. Allerdings konnte ich bislang keine Aussagen zum Support für 3PIP-Telefone finden. Diese basieren weiterhin auf Skype for Business und nutzen in der Cloud ein besonderes Gateway, damit ein "Teams Only" Benutzer mit Telefonie diese Geräte noch voraussichtlich bis Juni 2021 nutzen kann. Es gibt hier aber keinen Hotkey für "Call Unpark".

Auf einem 3PIP-Phone konnte ich einen geparkten Anruf nicht heranziehen. Ein einfaches Telefon in einer Werkhalle ist aktuell keine Lösung.

Fehlersuche Client

Natürlich protokolliert sowohl der Server als auch der Client die verschiedenen Aktionen. Über die Teams Logs auf dem Client (CTRL-AALT-SHIFT-1 drücken" ist eine schnelle Suche per Text-Editor möglich. Hier ein Auszug eines erfolgreichen Park-Vorgangs mit der Nummer 10:

2019-10-10T21:37:48.031Z Inf	calling-screen: isCallwithParkservice = undefined
2019-10-10T21:37:47.966Z Inf	callingService: startUnparkCall: Type_CallParticipantRemoved: call with id: <guid>, participant: 4:*11. Setting conversation id. thread id null, call type 1
2019-10-10T21:37:47.965Z Inf	callingParticipantService: participant removed: PSTN-xxxxxxxxxx, teamsCallId: 4
2019-10-10T21:37:47.716Z Inf	CallParkService: [unparkCall] Call unparked successfully. [call][callId=<guid>] [unparkContext=0]. unpark Code: 10
2019-10-10T21:37:47.716Z Inf	[Scenario]unpark_call [step](1)stop (4778ms/4779ms)
2019-10-10T21:37:47.657Z Inf	callingService: startUnparkCall: succeeded, parkContext 0, parkCode 10
2019-10-10T21:37:43.509Z Inf	callingAgents: calling-stack [JS.TsCalling.SignalingAgent] ffffffff: CSA[<guid>][Idle] [ksy09sa3][startOutgoingCall]options={
  "causeId": "ksy09sa3",
  "unpark": true,
  "context": "*11"
}
2019-10-10T21:37:43.058Z Inf	CallingUserActionService: placeCallInternal: [withVideo:undefined][conversationId:8:orgid:<guid>][isChannelMeeting:false][isVoicemail:undefined][isUnpark:true][isEchoBotCall:false]
2019-10-10T21:37:42.940Z Inf	CallingUserActionService: getEventData: [call][context={"context":"unpark_call"}]
2019-10-10T21:37:42.938Z Inf	CallingUserActionService: call enableEndpointHint=true, checkUserMode=undefined, isvoicemail=undefined, isUnpark=true 
2019-10-10T21:37:42.938Z Inf	[Scenario]unpark_call start
2019-10-10T21:37:42.937Z Inf	CallParkService: [unparkCall] Unpark code 10. [unparkContext=0]
2019-10-10T21:37:42.927Z Inf	UnparkCallController: Unparking call
2019-10-10T21:37:36.355Z Inf	dialogService: Opened the dialog: CallingUnparkCall
2019-10-10T21:37:36.354Z Inf	dialogService: Start opening the dialog: CallingUnparkCall
2019-10-10T21:36:03.537Z Inf	calling-screen: isCallwithParkservice = undefined
2019-10-10T21:36:03.448Z Inf	[Scenario]calling_emergency_service_start_call start
2019-10-10T21:36:03.336Z Inf	CallingUserActionService: call enableEndpointHint=true, checkUserMode=undefined, isvoicemail=undefined, isUnpark=undefined 
2019-10-10T21:36:01.094Z Inf	CallParkService: [deleteParkedCall][call] disposing timer subscriptions
2019-10-10T21:36:01.094Z Inf	CallParkService: [deleteParkedCall][call] deleting call from parked call from parkedCalls
2019-10-10T21:35:07.726Z Inf	AggregatedSettingsStore: initFromSettings: Fetched settings from local store for schema substratesearch_people: userPropertiesSettings,userResourcesSettings,tenantSettingsV2,callingPolicy,messagingPolicy,clientSettings,meetingPolicy,educationAssignmentsAppPolicy,feedbackPolicy,adminSettings,teamsAndChannelsPolicy,tenantVoiceSettings,policySettings,voiceAdminSettings,callParkPolicy,onlineDialinConferencingPolicy,broadcastMeetingPolicy
2019-10-10T21:35:01.964Z Inf	calling-screen: isCallwithParkservice = undefined
2019-10-10T21:34:50.565Z Inf	CallParkService: [parkCall] finally is being executed. Deleted [call][callId=<guid>]. [parkContext=0]
2019-10-10T21:34:50.550Z Inf	callingService: leaving call: [callId=<guid>][leaveCallContext=call_park_service][leaveCallState:4]
2019-10-10T21:34:50.549Z Inf	[Scenario]park_call [step](3)stop (17ms/3462ms)
2019-10-10T21:34:50.532Z Inf	[Scenario]park_call [step](2)call_parked (1999ms)
2019-10-10T21:34:50.532Z Inf	CallParkService: [parkCall] successfully parked with pickUp Code 10. [call][callId=<guid>][parkContext=0]

2019-10-10T21:34:48.534Z Inf	CallParkService: [parkCall] callbeing parked. [call][callId=<guid>] [parkContext=0]
2019-10-10T21:34:48.533Z Inf	[Scenario]park_call [step](1)call_held (1446ms)
2019-10-10T21:34:48.533Z Inf	CallParkService: [parkCall] Call put on hold using toggleHold. [call][callId=<guid>][parkContext=0]
2019-10-10T21:34:48.533Z Inf	[Scenario]hold_call [step](1)stop (1446ms/1446ms)
2019-10-10T21:34:48.531Z Inf	callingAgents: calling-stack [JS.TsCalling.SignalingAgent] ffffffff: CSA[<guid>][Connected]/[HTTP]/[588100af][Dispatcher] attempt to send request
2019-10-10T21:34:48.530Z Inf	CallTogglingService: [holdCall] Successfully held call (callId = <guid>)
2019-10-10T21:34:48.530Z Inf	CallBackgroundBlurMixinImplementation: Called resetBackgroundBlurringOnCallLeave from hold_call

Auf dem Server können Sie über das Call Quality Dashboard den Anruf verfolgen und wer die Rufnummern per Direct Routing angebunden hat, sieht ggfls. auch im Syslog des SBCs/Gateway die "REFER"-Pakete.

Trace auf dem SBC

Wenn Sie ihre Rufnummern selbst mitbringen und keinen Microsoft Dialplan haben, dann können Sie natürlich auch auf dem SBC die Funktion mit tracen. Hier ein Beispiel eines Anrufs von meinem Mobiltelefon auf die Teams-Rufnummer. Der Anruf wird dann gehalten und wieder vom gleichen Telefon aus der Parkschleife abgerufen. Sie gehen im wesentlichen drei Abschnitte:

Abschnitt Beschreibung

Verbindungsaufbau

Das ist noch der normale Verbindungsaufbau vom Amt zu Office 365, der mit einem finalen 200OK und ACK abgeschlossen wird. Die Verbindung steht.

Parken

Beim Parten sendet Teams nun einen INVITE mit der gleichen Call-ID zurück zum SBC. Der hier wichtige Ausschnitt im SDP ist der "Inactive"-Wert.

Unpark

 

Wird der geparkte Anruf nun von einem Teilnehmer wieder abgerufen, dann sendet Office 365 einen REFER zum SBC. Der REFER enthält einen "Contact"-Header, mit dem der SBC dann die Verbindung zum neuen Ziel aufbaut. Als neue Verbindung erscheint nun hier auch der neue Ziel-SBC. Das könnte auch ein anderer SBC sein.

Hier möchte ich es mit der Beschreibung zu Call Park nicht zu weit treiben. Als Reaktion auf den REFER sendet der SBC einen INVITE zu Teams, der aber keine SDP-Kandidaten enthält.

 

Daher sendet TEAMS seinerseits nun einen INVITE mit Kandidaten zum SDP zurück.

Am Ende wird der Call dann durch BYE von Teams beendet.

Weitere Links