iWrench

Python sip sample (PJSUA)

May 12th, 2021 (edited)
360
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # $Id$
  2.  
  3. import sys, os, time
  4. import pjsua as pj
  5. from serial import Serial
  6.  
  7. print ("Hello, this is "+sys.argv[0]+" by Sokoloff with parameters:")
  8.  
  9. for param in sys.argv:
  10.     print (param)
  11.  
  12. if len(sys.argv) < 9:
  13.     print ("run: python2.7 softphone.py dtmf1 dtmf2 account password domain port serial_port serial_speed")
  14.     exit(0)
  15.  
  16. dtmf1=sys.argv[1]
  17. dtmf2=sys.argv[2]
  18. domain=sys.argv[5]
  19. port=sys.argv[6]
  20. account=sys.argv[3]
  21. password=sys.argv[4]
  22.  
  23. PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT=120
  24.  
  25. current_call = None
  26. number = None
  27. call = None
  28.  
  29. def dial_dtmf(self, digits):
  30.  
  31.     lck = self._lib().auto_lock()
  32.     try:
  33.         self.dial_dtmf(digits+"#")
  34.         ser.write(":OK\r\n")
  35.     except:
  36.         ser.write(":STUCK\r\n")
  37.  
  38. def log_cb(level, str, len):
  39.     print("[LC] Says: \r\n"+ str)
  40.     if "SIP/2.0 100 Trying" in str:
  41.         for node in str.split("\n"):
  42.             if "To: <sip:" in node:
  43.                 tmp=node.split("@")[0]
  44.                 global number
  45.                 nr=tmp.split(":")[2]
  46.                 if nr.isdigit():
  47.                     if account in nr:
  48.                         number="SELF\r\n"
  49.                     else:
  50.                         number=nr
  51.                 else:
  52.                     number="SELF\r\n"
  53.                 print("[LC] DETECTING INCOMING TO "+number)
  54.  
  55. class IncomingCallCallback(pj.CallCallback):
  56.     def __init__(self, call=None):
  57.         pj.CallCallback.__init__(self, call)
  58.  
  59.     def on_state(self):
  60.         print ("[ICC] CALL STATE ", self.call.info().state_text)
  61.         print ("[ICC] LAST CODE ", self.call.info().last_code)
  62.         print ("[ICC] INFORMATION (" + self.call.info().last_reason + ")")
  63.         if "CONNECTING" in self.call.info().state_text:
  64.             global number
  65.             print ("[ICC] INFORMATION NUM IS (" + str(number) + ")")
  66.         if "DISCONNCTD" in self.call.info().state_text:
  67.             global current_call
  68.             global ser
  69.             global number
  70.             current_call = None
  71.             number = None
  72.             if "Clear by analog" in self.call.info().last_reason:
  73.                 ser.write(":OK\r\n")
  74.             else:
  75.                 ser.write(":CANCEL\r\n")
  76.     def on_media_state(self):
  77.         global lib
  78.         if self.call.info().media_state == pj.MediaState.ACTIVE:
  79.             call_slot = self.call.info().conf_slot
  80.             lib.conf_connect(call_slot, 0)
  81.             lib.conf_connect(0, call_slot)
  82.             print ("[ICC] ON_MEDIA_STATE SAYS THAT WE CAN TALK")
  83.     def on_dtmf_digit(self,digit):
  84.             print("[ICC] DTMF detected! Signal="+digit)
  85.             if dtmf1 in digit:
  86.                 ser.write(":OPEN1\r\n")
  87.             if dtmf2 in digit:
  88.                 ser.write(":OPEN2\r\n")
  89.  
  90. class OutgoingCallCallback(pj.CallCallback):
  91.     def __init__(self, call=None):
  92.         pj.CallCallback.__init__(self, call)
  93.  
  94.     def on_state(self):
  95.         print ("[OCC] CALL STATE ", self.call.info().state_text)
  96.         print ("[OCC] LAST CODE ", self.call.info().last_code)
  97.         print ("[OCC] INFORMATION (" + self.call.info().last_reason + ")")
  98.         if "DISCONNCTD" in self.call.info().state_text:
  99.             global current_call
  100.             global ser
  101.             global number
  102.             current_call = None
  103.             number = None
  104.             ser.write(":CANCEL\r\n")
  105.     def on_media_state(self):
  106.         global lib
  107.         if self.call.info().media_state == pj.MediaState.ACTIVE:
  108.             call_slot = self.call.info().conf_slot
  109.             lib.conf_connect(call_slot, 0)
  110.             lib.conf_connect(0, call_slot)
  111.             print ("[OCC] ON_MEDIA_STATE SAYS THAT WE CAN TALK")
  112.             global current_call
  113.             if "CONNECTING" in self.call.info().state_text:
  114.                 ser.write(":TALK\r\n")
  115.     def on_dtmf_digit(self,digit):
  116.             print("[OCC] DTMF detected! Signal="+digit)
  117.             if dtmf1 in digit:
  118.                 ser.write(":OPEN1\r\n")
  119.             if dtmf2 in digit:
  120.                 ser.write(":OPEN2\r\n")
  121. class SipAccountCallback(pj.AccountCallback):
  122.     def __init__(self, acc):
  123.         pj.AccountCallback.__init__(self, acc)
  124.     def onRegState(self, prm):
  125.         print ("[SAC] REG STATE CHANGED: " + prm.reason)
  126.     def on_incoming_call(self, call):
  127.         global current_call
  128.         if current_call:
  129.             call.answer(486, "Busy")
  130.             ser.write(":BUSY\r\n")
  131.             return
  132.         print "[SAC] INCOMING CALL FROM: ", call.info().remote_uri
  133.         current_call = call
  134.         current_call.set_callback(IncomingCallCallback(current_call))
  135.         global ser
  136.         ser.write(":"+str(number)+"\r\n")
  137.         #need to answer! waiting :talk from serial
  138. try:
  139.  
  140.     #init pjsua and serial port
  141.  
  142.     lib = pj.Lib()
  143.     ser = Serial()
  144.  
  145.     lib.init(log_cfg = pj.LogConfig(level=6, callback=log_cb))
  146.     transport = lib.create_transport(pj.TransportType.UDP)
  147.     ser.baudrate = sys.argv[8]
  148.     ser.port = sys.argv[7]
  149.  
  150.     lib.start()
  151.     ser.open()
  152.  
  153.     if not ser.isOpen():
  154.        raise SystemExit
  155.  
  156.     #init sip account
  157.  
  158.     acc_conf = pj.AccountConfig(domain = domain+':'+port, username = account, password = password, display = account)
  159.     acc_conf.id ="sip:"+account
  160.     acc_conf.reg_uri ='sip:'+domain
  161.     acc_conf.ka_data = '\r\n'
  162.     acc_conf.ka_interval = 15
  163.     acc_callback = SipAccountCallback(acc_conf)
  164.     acc = lib.create_account(acc_conf,cb=acc_callback)
  165.     acc.set_callback(acc_callback)
  166.  
  167.     #listen for incoming or serial commands
  168.  
  169.     while True:
  170.  
  171.         #monitoring registration status
  172.  
  173.         t=0
  174.         while acc.info().reg_status !=200:
  175.             print ("[MAIN] Registering... Now status is `"  + acc.info().reg_reason + "` COUNTER = "+str(t))
  176.             t=t+1
  177.             if t == 20:
  178.                 sys.exit(1)
  179.             if acc.info().reg_status !=200:
  180.                 ser.write(":REGTRY\r\n")
  181.             else:
  182.                 ser.write(":REGSUCC\r\n")
  183.             time.sleep(1)
  184.  
  185.         read_serial = ser.readline()
  186.  
  187.         # incoming from serial
  188.         if ":talk" in read_serial:
  189.             if number and current_call:
  190.                 current_call.answer(200,"Ok")
  191.                 ser.write(":OK\r\n")
  192.         if ":cancel" in read_serial:
  193.                 if current_call:
  194.                     current_call.hangup(603, "Hangup talk reason: Reset by serial with :cancel", None )
  195.                 if call:
  196.                     call.hangup(603, "Hangup ringing reason: Reset by serial with :cancel", None )
  197.                 ser.write(":OK\r\n")
  198.                 number = None
  199.                 current_call = None
  200.                 call = None
  201.         if ":open1" in read_serial:
  202.                 dial_dtmf(current_call,dtmf1)
  203.         if ":open2" in read_serial:
  204.                 dial_dtmf(current_call,dtmf2)
  205.         if ":busy" in read_serial:
  206.                 #Added 12/05/2021
  207.                 print(":busy")
  208.                 if call:
  209.                     call.answer(486, "Busy")
  210.                                         call.hangup(603, "Hangup ringing reason: Reset by serial with :busy", None )
  211.                                     if current_call:
  212.                                             current_call.hangup(603, "Hangup talk reason: Reset by serial with :busy", None )
  213.                     ser.write(":OK\r\n")
  214.                 else:
  215.                     ser.write(":ERR_NO-CALL-NOW\r\n")
  216.         if ":0" in read_serial:
  217.             if  acc.info().reg_status !=200:
  218.                 ser.write(":OK\r\n")
  219.             else:
  220.                 ser.write(":OK\r\n")
  221.                 ser.write(":CANCEL\r\n")
  222.         else:
  223.             num = read_serial.split(":")
  224.             if len(num) > 1:
  225.                 mnum=num[1].replace("\r\n","")
  226.                 if mnum.isdigit():
  227.                     print("[MAIN] ANALOG -> SIP CALL. RINGING TO  "+mnum)
  228.                     call = acc.make_call("sip:"+mnum+"@"+domain+":"+port, OutgoingCallCallback())
  229.                     if call:
  230.                         ser.write(":OK\r\n")
  231.                     else:
  232.                         ser.write(":NOTFOUND\r\n")
  233. except pj.Error, e:
  234.     print ("Exception: " + str(e))
  235.     if lib:
  236.         lib.destroy()
  237.         lib = None
  238.         ser.close()
  239. finally:
  240.     time.sleep(1)
  241.     if current_call:
  242.         current_call.hangup(603,"Terminate call. Reason: Terminating softphone.", None)
  243.     if call:
  244.         call.hangup(603,"Hangup call. Reason: Terminating softphone.", None)
  245.     if lib:
  246.         lib.destroy()
  247.         lib = None
  248.     if ser:
  249.         ser.close()
  250.  
  251. sys.exit(1)
  252.  
RAW Paste Data