def self_check(self, *args, **kwargs): self.lib.thread_register('pjsip_handle_events') self.lib.handle_events(self.call_timeout) if self.current_call is not None: if self.current_call.is_valid() is 0: del self.current_callcallback self.current_callcallback = None del self.current_call self.current_call = None try: if self.current_call.info().call_time == 0 \ and self.current_call.info().total_time > self.call_timeout: logger.info( "call timeout - hangup current call after %s seconds", self.call_timeout) self.current_call.hangup() DoorPi().event_handler('OnSipPhoneCallTimeoutNoResponse', __name__) if self.current_call.info().call_time > self.max_call_time: logger.info( "max call time reached - hangup current call after %s seconds", self.max_call_time) self.current_call.hangup() DoorPi().event_handler('OnSipPhoneCallTimeoutMaxCalltime', __name__) except: pass
def __init__(self, *args, **kwargs): logger.debug("__init__") DoorPi().event_handler.register_event('OnSipPhoneCreate', __name__) DoorPi().event_handler.register_event('OnSipPhoneStart', __name__) DoorPi().event_handler.register_event('OnSipPhoneDestroy', __name__) DoorPi().event_handler.register_event('OnSipPhoneRecorderCreate', __name__) DoorPi().event_handler.register_event('OnSipPhoneRecorderDestroy', __name__) DoorPi().event_handler.register_event('BeforeSipPhoneMakeCall', __name__) DoorPi().event_handler.register_event('OnSipPhoneMakeCall', __name__) DoorPi().event_handler.register_event('AfterSipPhoneMakeCall', __name__) DoorPi().event_handler.register_event( 'OnSipPhoneCallTimeoutNoResponse', __name__) DoorPi().event_handler.register_event( 'OnSipPhoneCallTimeoutMaxCalltime', __name__) self.__Lib = None self.__account = None self.current_callcallback = None self.current_account_callback = None self.__recorder = None self.__player = None self.call_timeout = 30
def call(self, number): DoorPi().event_handler('BeforeSipPhoneMakeCall', __name__, {'number':number}) logger.debug("call (%s)",str(number)) if not self.current_call: logger.debug('no current call -> start new call') self.reset_call_start_datetime() if self.core.invite_with_params(number, self.base_config) is None: if DoorPi().event_handler.db.get_event_log_entries_count('OnSipPhoneMakeCallFailed') > 5: logger.error('failed to execute call five times') else: DoorPi().event_handler('OnSipPhoneMakeCallFailed', __name__, {'number':number}) return None DoorPi().event_handler('OnSipPhoneMakeCall', __name__, {'number':number}) elif number in self.current_call.remote_address.as_string_uri_only(): if self.current_call_duration <= 2: logger.debug("same call %s again while call is running since %s seconds? -> skip", self.core.current_call.remote_address.as_string_uri_only(), self.current_call_duration ) else: logger.debug("press twice with call duration > 1 second? Want to hangup current call? OK...") self.core.terminate_all_calls() else: logger.debug("new call needed? hangup old first...") self.core.terminate_all_calls() self.call(number) DoorPi().event_handler('AfterSipPhoneMakeCall', __name__, {'number':number}) return self.current_call
def __init__(self, whitelist = [], *args, **kwargs): logger.debug("__init__") DoorPi().event_handler.register_action('OnShutdown', self.destroy) DoorPi().event_handler.register_event('OnSipPhoneCreate', __name__) DoorPi().event_handler.register_event('OnSipPhoneStart', __name__) DoorPi().event_handler.register_event('OnSipPhoneDestroy', __name__) self.__recorder = DummyRecorder()
def onIncomingCall(self, iprm: pj.OnIncomingCallParam) -> None: sp = DoorPi().sipphone call = CallCallback(self, iprm.callId) callInfo = call.getInfo() oprm = pj.CallOpParam(False) event = None fire_event("BeforeCallIncoming", remote_uri=callInfo.remoteUri) try: if not sp.is_admin(callInfo.remoteUri): logger.info("Rejecting call from unregistered number %s", callInfo.remoteUri) oprm.statusCode = pj.PJSIP_SC_FORBIDDEN event = "OnCallReject" return with sp._Pjsua2__call_lock: if sp.current_call is not None and sp.current_call.isActive(): logger.info("Busy-rejecting call from %s", callInfo.remoteUri) oprm.statusCode = pj.PJSIP_SC_BUSY_HERE event = "OnCallBusy" return else: logger.info("Accepting incoming call from %s", callInfo.remoteUri) oprm.statusCode = pj.PJSIP_SC_OK event = "OnCallIncoming" sp.current_call = call return finally: call.answer(oprm) fire_event(event, remote_uri=callInfo.remoteUri)
def onCallMediaState(self, prm: pj.OnCallMediaStateParam) -> None: ci = self.getInfo() if ci.state != pj.PJSIP_INV_STATE_CONFIRMED: logger.debug("Ignoring media change in call to %s", repr(ci.remoteUri)) return adm = pj.Endpoint.instance().audDevManager() logger.debug("Call to %s: media changed", repr(ci.remoteUri)) audio, video = self.__getAudioVideoMedia() if audio: # Connect call audio to speaker and microphone audio.startTransmit(adm.getPlaybackDevMedia()) adm.getCaptureDevMedia().startTransmit(audio) # Apply capture and ring tone loudness conf = DoorPi().config playback_loudness = conf.get_float(SIPPHONE_SECTION, "playback_loudness", 1.0) capture_loudness = conf.get_float(SIPPHONE_SECTION, "capture_loudness", 1.0) logger.trace("Adjusting RX level to %01.1f", playback_loudness) logger.trace("Adjusting TX level to %01.1f", capture_loudness) audio.adjustRxLevel(playback_loudness) audio.adjustTxLevel(capture_loudness) else: logger.error("Call to %s: no audio media", repr(ci.remoteUri))
def __init__(self): self.__record_filename = DoorPi().config.get( SIPPHONE_SECTION, 'records', '!BASEPATH!/records/%Y-%m-%d_%H-%M-%S.wav') if self.__record_filename is '': logger.debug( 'no recorder found in config at section DoorPi and key records' ) return DoorPi().event_handler.register_action('OnSipPhoneDestroy', self.destroy) DoorPi().event_handler.register_event('OnRecorderStarted', __name__) DoorPi().event_handler.register_event('OnRecorderStopped', __name__) DoorPi().event_handler.register_event('OnRecorderCreated', __name__) if DoorPi().config.get_bool(SIPPHONE_SECTION, 'record_while_dialing', 'False') is True: DoorPi().event_handler.register_action('OnSipPhoneMakeCall', self.start) else: DoorPi().event_handler.register_action('OnCallStateConnect', self.start) DoorPi().event_handler.register_action('OnCallStateDisconnect', self.stop) DoorPi().event_handler('OnRecorderCreated', __name__)
def destroy(self): logger.debug("destroy") self.core.terminate_all_calls() DoorPi().event_handler.fire_event_synchron('OnSipPhoneDestroy', __name__) DoorPi().event_handler.unregister_source(__name__, True) return
def stop(self): if not DoorPi().sipphone.current_call: return logger.debug('stopping recording to %s', self.__last_record_filename) DoorPi().sipphone.current_call.stop_recording() DoorPi().event_handler( 'OnRecorderStopped', __name__, {'last_record_filename': self.__last_record_filename})
def __init__(self): DoorPi().event_handler.register_action('OnSipPhoneDestroy', self.destroy) DoorPi().event_handler.register_event('OnRecorderStarted', __name__) DoorPi().event_handler.register_event('OnRecorderStopped', __name__) DoorPi().event_handler.register_event('OnRecorderCreated', __name__) DoorPi().event_handler('OnRecorderCreated', __name__)
def call(self, number): DoorPi().event_handler('BeforeSipPhoneMakeCall', __name__, {'number': number}) logger.debug('call (%s)', str(number)) if not self.current_call: self.reset_call_start_datetime() if self.core.invite_with_params(number, self.base_config) is None: if DoorPi().event_handler.db.get_event_log_entries_count('OnSipPhoneMakeCallFailed') > 5: logger.error('failed to execute call five times') else: DoorPi().event_handler('OnSipPhoneMakeCallFailed', __name__, {'number': number}) return None DoorPi().event_handler('OnSipPhoneMakeCall', __name__, {'number': number}) elif number in self.current_call.remote_address.as_string_uri_only(): if self.current_call_duration <= 2: logger.debug('same call %s again while call is running since %s seconds', self.core.current_call.remote_address.as_string_uri_only(), self.current_call_duration) else: logger.debug('pressed twice with call duration > 1 second. Hangup current calls.") self.core.terminate_all_calls() else: logger.debug('only one call at the same time possible') self.core.terminate_all_calls() self.call(number) DoorPi().event_handler('AfterSipPhoneMakeCall', __name__, {'number': number}) return self.current_call
def call_state_changed(self, core, call, call_state, message): self.call_state_changed_handle(core, call, call_state, message) if core.calls_nb > 0 and self._last_number_of_calls == 0: DoorPi().event_handler('OnMediaRequired', __name__) elif self._last_number_of_calls is not core.calls_nb: DoorPi().event_handler('OnMediaNotRequired', __name__) self._last_number_of_calls = core.calls_nb
def recorder_config() -> dict: conf = DoorPi().config return { "path": conf.get_string_parsed(SIPPHONE_SECTION, "record_path", ""), "early": conf.get_bool(SIPPHONE_SECTION, "record_while_dialing", True), "keep": conf.get_int(SIPPHONE_SECTION, "record_keep", 10), }
def dialtone_config() -> dict: conf = DoorPi().config return { "filename": conf.get_string_parsed(SIPPHONE_SECTION, "dialtone", "!BASEPATH!/media/dialtone.wav"), "loudness": conf.get_float(SIPPHONE_SECTION, "dialtone_loudness", 1.0) }
def stop(self): if self.__rec_id is not None: DoorPi().sipphone.lib.thread_register('PjsuaPlayer_start_thread') logger.debug('stopping recording to %s', self.__last_record_filename) DoorPi().sipphone.lib.conf_disconnect(0, self.__slot_id) DoorPi().sipphone.lib.recorder_destroy(self.__rec_id) self.__rec_id = None self.__slot_id = None DoorPi().event_handler('OnRecorderStopped', __name__)
def __init__(self): eh = DoorPi().event_handler for ev in [ # Fired by this class "OnSIPPhoneCreate", "OnSIPPhoneStart", "OnSIPPhoneDestroy", "OnCallOutgoing", "OnCallOutgoing_S", # Fired by AccountCallback "BeforeCallIncoming", "OnCallIncoming", "OnCallBusy", "OnCallReject", "BeforeCallIncoming_S", "OnCallIncoming_S", "OnCallBusy_S", "OnCallReject_S", # Fired by CallCallback (all) and Worker (disconnect) "OnCallConnect", "OnCallDisconnect", "OnCallConnect_S", "OnCallDisconnect_S", ]: eh.register_event(ev, EVENT_SOURCE) # register DTMF events, fired by CallCallback for dtmf in DoorPi().config.get_keys("DTMF"): eh.register_event(f"OnDTMF_{dtmf}", EVENT_SOURCE) self.__ep = None self.__waiting_calls = [] # outgoing calls that are not yet connected self.__ringing_calls = [] # outgoing calls that are currently ringing self.__call_lock = threading.Lock() self.current_call = None self.dialtone = None # Python doesn't like being called from native threads, so we # need our own worker. self.__worker = None self.__worker_thread = None fire_event("OnSIPPhoneCreate", async_only=True) eh.register_action("OnShutdown", self.__del__)
def dtmf_received(self, core, call, digits): logger.debug("on_dtmf_digit (%s)", str(digits)) digits = chr(digits) DoorPi().event_handler('OnDTMF', __name__, {'digits':digits}) self.__DTMF += str(digits) for DTMF in self.__possible_DTMF: if self.__DTMF.endswith(DTMF[1:-1]): DoorPi().event_handler('OnDTMF_'+DTMF+'', __name__, { 'remote_uri': str(call.remote_address.as_string_uri_only()), 'DTMF': str(self.__DTMF) })
def onCallState(self, prm: pj.OnCallStateParam) -> None: ci = self.getInfo() sp = DoorPi().sipphone eh = DoorPi().event_handler conf = DoorPi().config if ci.state == pj.PJSIP_INV_STATE_CALLING: logger.debug("Call to %s is now calling", repr(ci.remoteUri)) elif ci.state == pj.PJSIP_INV_STATE_INCOMING: logger.debug("Call from %s is incoming", repr(ci.remoteUri)) elif ci.state == pj.PJSIP_INV_STATE_EARLY: logger.debug("Call to %s is in early state", repr(ci.remoteUri)) elif ci.state == pj.PJSIP_INV_STATE_CONNECTING: logger.debug("Call to %s is now connecting", repr(ci.remoteUri)) elif ci.state == pj.PJSIP_INV_STATE_CONFIRMED: logger.info("Call to %s was accepted", repr(ci.remoteUri)) self.__fire_disconnect = True with sp._Pjsua2__call_lock: prm = pj.CallOpParam() if sp.current_call is not None: # (note: this should not be possible) sp.current_call.hangup(prm) sp.current_call = None sp.current_call = self for ring in sp._Pjsua2__ringing_calls: if self != ring: ring.hangup(prm) sp._Pjsua2__ringing_calls = [] sp._Pjsua2__waiting_calls = [] fire_event("OnCallConnect", remote_uri=ci.remoteUri) elif ci.state == pj.PJSIP_INV_STATE_DISCONNECTED: logger.info("Call to %s disconnected after %d seconds (%d total)", repr(ci.remoteUri), ci.connectDuration.sec, ci.totalDuration.sec) with sp._Pjsua2__call_lock: if sp.current_call == self: sp.current_call = None elif self in sp._Pjsua2__ringing_calls: sp._Pjsua2__ringing_calls.remove(self) if self.__fire_disconnect: logger.trace("Firing disconnect event for call to %s", repr(ci.remoteUri)) fire_event("OnCallDisconnect", remote_uri=ci.remoteUri) elif len(sp._Pjsua2__ringing_calls) == 0: logger.trace( "Last ringing call disconnected, synthesizing disconnect" ) fire_event("OnCallDisconnect", remote_uri="sip:null@null") else: logger.trace("Skipping disconnect event for call to %s", repr(ci.remoteUri)) else: logger.warning("Call to %s: unknown state %d", repr(ci.remoteUri), ci.state)
def __init__(self, path, early, keep): self.__path = path self.__early = early self.__keep = keep self.__recorder = None eh = DoorPi().event_handler eh.register_action("OnCallOutgoing_S", self.startEarly) eh.register_action("OnCallConnect_S", self.start) eh.register_action("OnCallDisconnect_S", self.stop) eh.register_action("OnCallDisconnect", self.cleanup) if self.__path: logger.debug("Call recording destination: %s", self.__path)
def __init__(self, account=None): pj.AccountCallback.__init__(self, account) DoorPi().event_handler.register_event('BeforeCallIncoming', __name__) DoorPi().event_handler.register_event('OnCallReconnect', __name__) DoorPi().event_handler.register_event('AfterCallReconnect', __name__) DoorPi().event_handler.register_event('OnCallBusy', __name__) DoorPi().event_handler.register_event('AfterCallBusy', __name__) DoorPi().event_handler.register_event('OnCallIncoming', __name__) DoorPi().event_handler.register_event('AfterCallIncoming', __name__) DoorPi().event_handler.register_event('OnCallReject', __name__) DoorPi().event_handler.register_event('AfterCallReject', __name__)
def __init__(self, sipphone): self.__phone = sipphone self.__ep = None conf = DoorPi().config self.__call_timeout = conf.get_int(SIPPHONE_SECTION, "call_timeout", 15) self.__max_call_time = conf.get_int(SIPPHONE_SECTION, "max_call_time", 120) self.running = True self.error = None self.ready = threading.Semaphore(0) self.wake = threading.Condition() self.hangup = 0
def __init__(self, acc: AccountCallback, callId=pj.PJSUA_INVALID_ID): logger.trace("Constructing call with callId %s", callId) super().__init__(acc, callId) self.__DTMF = "" self.__possible_DTMF = DoorPi().config.get_keys("DTMF") self.__fire_disconnect = False
def is_admin_number(self, remote_uri=None): logger.debug("is_admin_number (%s)", remote_uri) if remote_uri is None: if self.current_call is not None: remote_uri = self.current_call.info().remote_uri else: logger.debug( "couldn't catch current call - no parameter and no current_call from doorpi itself" ) return False possible_admin_numbers = DoorPi().config.get_keys('AdminNumbers') for admin_number in possible_admin_numbers: if admin_number == "*": logger.info( "admin numbers are deactivated by using '*' as single number" ) return True if "sip:" + admin_number + "@" in remote_uri: logger.debug("%s is an adminnumber", remote_uri) return True if "sip:" + admin_number is remote_uri: logger.debug("%s is adminnumber %s", remote_uri, admin_number) return True logger.debug("%s is not an adminnumber", remote_uri) return False
def is_admin(self, uri: str) -> bool: try: canonical_uri = self.canonicalize_uri(uri) except ValueError: return False conf = DoorPi().config section = "SIP-Admin" for admin_number in conf.get_keys(section): if admin_number == "*": logger.trace("Found '*' in config: everything is an admin number") return True elif canonical_uri == self.canonicalize_uri(admin_number) \ and conf.get_string(section, admin_number) == "active": logger.trace("%s is admin number %s", uri, admin_number) return True logger.trace("%s is not an admin number", uri) return False
def on_media_state(self): logger.debug("on_media_state (%s)", str(self.call.info().media_state)) DoorPi().event_handler( 'OnCallMediaStateChange', __name__, { 'remote_uri': self.call.info().remote_uri, 'media_state': str(self.call.info().media_state) })
def call(self, number): DoorPi().event_handler('BeforeSipPhoneMakeCall', __name__, {'number': number}) logger.debug("call(%s)", str(number)) self.lib.thread_register('call_theard') sip_server = doorpi.sipphone.pjsua_lib.Config.sipphone_server() sip_uri = "sip:" + str(number) + "@" + str(sip_server) if self.lib.verify_sip_url(sip_uri) is not 0: logger.warning("SIP-URI %s is not valid (Errorcode: %s)", sip_uri, self.lib.verify_sip_url(sip_uri)) return False else: logger.debug("SIP-URI %s is valid", sip_uri) DoorPi().event_handler('OnSipPhoneMakeCall', __name__) if not self.current_call or self.current_call.is_valid() is 0: lck = self.lib.auto_lock() self.current_callcallback = SipPhoneCallCallBack() self.current_call = self.__account.make_call( sip_uri, self.current_callcallback) del lck elif self.current_call.info().remote_uri == sip_uri: if self.current_call.info().total_time <= 1: logger.debug( "same call again while call is running since %s seconds? -> skip", str(self.current_call.info().total_time)) else: logger.debug( "press twice with call duration > 1 second? Want to hangup current call? OK..." ) #self.current_call.hangup() self.lib.hangup_all() else: logger.debug("new call needed? hangup old first...") try: # self.current_call.hangup() self.lib.hangup_all() except pj.Error as e: logger.exception("Exception: %s", str(e)) self.call(Number) DoorPi().event_handler('AfterSipPhoneMakeCall', __name__) return self.current_call
def destroy(self): DoorPi().event_handler('OnSipPhoneDestroy', __name__) if self.lib is not None: self.lib.handle_events() self.__Lib.destroy() self.lib.handle_events() try: timeout = 0 while timeout < 5 and self.__Lib is not None: sleep(0.1) timeout += 0.1 self.lib.handle_events() except: DoorPi().event_handler.unregister_source(__name__, True) return
def self_check(self, *args, **kwargs): if not self.core: return self.core.iterate() if not self.current_call: return if self.current_call.state < lin.CallState.Connected: if self.current_call_duration > self.core.inc_timeout - 0.5: logger.info("call timeout - hangup current call after %s seconds (max. %s)", self.current_call_duration, self.core.inc_timeout) self.core.terminate_all_calls() DoorPi().event_handler('OnSipPhoneCallTimeoutNoResponse', __name__) else: if int(self.current_call_duration) > self.core.in_call_timeout - 0.5: logger.info("max call time reached - hangup current call after %s seconds (max. %s)", self.current_call_duration, self.core.in_call_timeout) self.core.terminate_all_calls() DoorPi().event_handler('OnSipPhoneCallTimeoutMaxCalltime', __name__)
def start(self): if self.__record_filename is '': return if self.__record_filename is not '': if not os.path.exists(os.path.dirname( self.__last_record_filename)): logger.info('Path %s does not exist - creating it now', os.path.dirname(self.__last_record_filename)) os.makedirs(os.path.dirname(self.__last_record_filename)) logger.debug('starting recording to %s', self.__last_record_filename) DoorPi().sipphone.current_call.start_recording() DoorPi().event_handler( 'OnRecorderStarted', __name__, {'last_record_filename': self.__last_record_filename})
def start(self): DoorPi().event_handler('OnSipPhoneCreate', __name__) self.__Lib = pj.Lib.instance() if self.__Lib is None: self.__Lib = pj.Lib() logger.debug("init Lib") self.__Lib.init( ua_cfg = pjsua_lib.Config.create_UAConfig(), media_cfg = pjsua_lib.Config.create_MediaConfig(), log_cfg = pjsua_lib.Config.create_LogConfig() ) logger.debug("init transport") transport = self.__Lib.create_transport( type = pj.TransportType.UDP, cfg = pjsua_lib.Config.create_TransportConfig() ) logger.debug("Listening on: %s",str(transport.info().host)) logger.debug("Port: %s",str(transport.info().port)) logger.debug("Lib.start()") self.lib.start(0) DoorPi().event_handler.register_action( event_name = 'OnTimeTick', action_object = 'pjsip_handle_events:50' ) logger.debug("init Acc") self.current_account_callback = pjsua_lib.SipPhoneAccountCallBack.SipPhoneAccountCallBack() self.__account = self.__Lib.create_account( acc_config = pjsua_lib.Config.create_AccountConfig(), set_default = True, cb = self.current_account_callback ) self.call_timeout = pjsua_lib.Config.call_timeout() self.max_call_time = pjsua_lib.Config.max_call_time() DoorPi().event_handler('OnSipPhoneStart', __name__) self.__recorder = pjsua_lib.Recorder.PjsuaRecorder() self.__player = pjsua_lib.Player.PjsuaPlayer() logger.debug("start successfully")