Nach einer längeren Testphase von 3cx, stellte sich heraus, dass 3CX bei allen Themen rund um Presence nur am eigenen Sofphone richtig funktioniert. Völlig uninteressant wenn man ein echtes VOIP-Telefon hat, dass die Funktionen ordnetlich abbilden könnte.
freepbx ist da out-of-the-box wesentlich besser zu steuern.
DND war relativ einfach umzusetzen, da die hints schon vorhanden waren. Ich habe aber einen anderen Status verwendet, um echtes Läuten des Telefons und DND zu unterscheiden.
nano /etc/asterisk/extensions_override_freepbx.conf
[app-dnd-on] include => app-dnd-on-custom exten => *78,1,Macro(user-callerid,) exten => *78,n,Set(CONNECTEDLINE(name-charset,i)=utf8) exten => *78,n,Set(CONNECTEDLINE(name,i)=Do Not Disturb: ON) exten => *78,n,Set(CONNECTEDLINE(num,i)=${AMPUSER}) exten => *78,n,Answer exten => *78,n,Wait(1) exten => *78,n,Set(DB(DND/${AMPUSER})=YES) exten => *78,n,Set(STATE=ONHOLD) exten => *78,n,Gosub(app-dnd-on,sstate,1()) exten => *78,n(hook_1),Playback(do-not-disturb&activated) exten => *78,n,Macro(hangupcall,)
Warum auch immer werden extesions von freepbx korrekt als unavailable erkannt, wenn die Identity sich abmeldet, aber der Status des hints hüpft nicht auf Unavailable.
Also schnell ein kleines python script gebaut, das alle 5 Sekunden die extensions kontrolliert und den hint aktualisiert wenn notwendig. Ginge vermutlich sauberer, wenn ein Script dauernd läuft und das event abfängt, aber egal.
""" Example to get list of active channels """ import asterisk.manager import sys endpoints = {12,13,14,15,16,17,18} manager = asterisk.manager.Manager() check_update = [] #extensions to check for update update = [] #extensions to update try: # connect to the manager try: manager.connect('localhost') manager.login('logoff_user', 'PASSWORD') # get a status report response = manager.status() #print(response) response = manager.command('pjsip list endpoints') #print(response.data) data = response.data for endpoint in endpoints: start = str(response.data.encode('utf-8')).find(str(endpoint)+'/'+str(endpoint)) end = start + 70 test = data[start:end].find('Unavailable') if test > -1: #extension is Unavailable check_update.append(endpoint) ###### check if hint is already set to unavailable, if not update import pdb pdb.set_trace() response = manager.command('core show hints') #print(response.data) data = response.data for endpoint in check_update: start = str(response.data.encode('utf-8')).find(str(endpoint)+'@ext-local') end = start + 60 test = data[start:end].find('Unavailable') if test == -1: #extension is Unavailable update.append(endpoint) for extension in update: response = manager.command('devstate change Custom:DND'+str(extension)+' UNAVAILABLE') #print(response.data) manager.logoff() except asterisk.manager.ManagerSocketException as e: print "Error connecting to the manager: %s" % e.strerror sys.exit(1) except asterisk.manager.ManagerAuthException as e: print "Error logging in to the manager: %s" % e.strerror sys.exit(1) except asterisk.manager.ManagerException as e: print "Error: %s" % e.strerror sys.exit(1) finally: # remember to clean up manager.close()
weil das Script alle 5 Sekunden laufen soll, hilft mir cron nicht, das kann nur ganze Minuten. Hier habe ich aber was gefunden das wunderbar klappt: https://stackoverflow.com/questions/9619362/running-a-cron-every-30-seconds
und dann noch die handgeschnitzte XML Definition für die Tasten:
<general type="AsteriskPresenceField"/> <initialization> <state value='initial'/> <variable name='subscr_ext' value='12'/> <variable name='subscr_proxy' value='xxx.xxx.xxx.xxx'/> <variable name='pickup_code' value='*8$(subscr_ext)'/> <variable name='subscr_uri' value='sip:$(subscr_ext)@$(subscr_proxy)'/> <identity value='1'/> </initialization> <subscription type="presence" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/presence[@entity="$(subscr_uri):5060"]</level1> <!-- OKAY--> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="CALLING">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/presence/note[.="Ringing"]</level2><!--OKAY--> <level2-1 translates_to="CALLING">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="IN_A_CALL">/presence/note[.="On the phone"]</level3><!-- OKAY--> <level4 translates_to="NOT_IN_USE">/presence/note[.="Ready"]</level4><!-- OKAY--> <level5 translates_to="OFFLINE">/presence/tuple/status/basic[.="closed"]</level5><!-- OKAY--> <level6 translates_to="DND">/presence/note[.="On hold"]</level6><!-- OKAY--> <level7 translates_to="free"/> </NotifyParsingRules> <NotifyParsingRules type="variable" id="call_id" state="ringing"> <level1 fetch_attribute="call-id">/dialog-info/dialog[@call-id]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_tag" state="ringing"> <level1 fetch_attribute="remote-tag">/dialog-info/dialog[@remote-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="local_tag" state="ringing"> <level1 fetch_attribute="local-tag">/dialog-info/dialog[@local-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_uri" state="ringing"> <level1 fetch_attribute="uri">/dialog-info/dialog/remote/target[@uri]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_name" state="ringing"> <level1 fetch_attribute="display">/dialog-info/dialog/remote/identity[@display]</level1> </NotifyParsingRules> <action> <invite target="$(remote_name)<$(remote_uri)>" when="on press" state="ringing" request_uri="$(remote_uri)" replaces="$(call_id);to-tag=$(remote_tag);from-tag=$(local_tag)"/> <dial target="$(subscr_uri)" when="on press"/> </action>