2015-05-18 14:34:45 +02:00
|
|
|
import time
|
|
|
|
import threading
|
2015-05-26 23:11:05 +02:00
|
|
|
import subprocess
|
2020-12-28 20:54:19 +01:00
|
|
|
from pylinphone.pylinphone import LinphoneCommunicationSocket
|
2015-05-18 14:34:45 +02:00
|
|
|
|
2017-04-28 18:57:43 +02:00
|
|
|
|
2015-05-28 23:44:35 +02:00
|
|
|
class PhoneProxyConfiguration(object):
|
2015-06-04 18:42:08 +02:00
|
|
|
def __init__(self, name, proxy, identity, username, password, realm,
|
|
|
|
prefix):
|
|
|
|
self.name = name
|
|
|
|
self.proxy = proxy
|
|
|
|
self.identity = identity
|
|
|
|
self.username = username
|
|
|
|
self.password = password
|
|
|
|
self.realm = realm
|
|
|
|
self.prefix = prefix
|
2015-05-28 23:44:35 +02:00
|
|
|
|
2017-04-28 18:57:43 +02:00
|
|
|
|
2015-05-28 23:44:35 +02:00
|
|
|
class PhoneConfiguration(object):
|
2015-06-04 18:42:08 +02:00
|
|
|
def __init__(self, sound_device, incoming_timeout, linphone_config,
|
|
|
|
default_proxy, proxies, stun_server):
|
|
|
|
self.sound_device = sound_device
|
|
|
|
self.incoming_timeout = incoming_timeout
|
|
|
|
self.linphone_config = linphone_config
|
|
|
|
self.default_proxy = default_proxy
|
|
|
|
self.proxies = proxies
|
|
|
|
self.stun_server = stun_server
|
2015-05-28 23:44:35 +02:00
|
|
|
|
2017-04-28 18:57:43 +02:00
|
|
|
|
2015-05-18 14:34:45 +02:00
|
|
|
class PhoneEvent(object):
|
|
|
|
RegInProgress,\
|
|
|
|
RegSuccessfull,\
|
2015-05-20 01:11:00 +02:00
|
|
|
RegLost,\
|
2015-05-18 14:34:45 +02:00
|
|
|
CallIncoming,\
|
|
|
|
CallRinging,\
|
|
|
|
CallAccepted,\
|
|
|
|
CallEnded,\
|
2015-05-18 21:39:53 +02:00
|
|
|
CallBusy,\
|
2015-05-19 23:11:11 +02:00
|
|
|
CallInvalidNumber = range(9)
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def string(cls, val):
|
2020-12-28 00:50:50 +01:00
|
|
|
for k, v in vars(cls).items():
|
2015-05-18 14:34:45 +02:00
|
|
|
if v == val:
|
|
|
|
return k
|
|
|
|
|
2017-04-28 18:57:43 +02:00
|
|
|
|
2015-05-18 14:34:45 +02:00
|
|
|
class PhoneInterface(object):
|
2021-02-24 18:30:15 +01:00
|
|
|
def __init__(self, config):
|
2015-05-18 14:34:45 +02:00
|
|
|
self.__event_cbs = []
|
|
|
|
|
2015-05-28 23:44:35 +02:00
|
|
|
self.__config = config
|
2020-12-28 20:55:34 +01:00
|
|
|
self.__core = LinphoneCommunicationSocket("/var/tmp/debian-sid/tmp/linphone")
|
2015-05-28 23:44:35 +02:00
|
|
|
|
2020-12-28 21:01:41 +01:00
|
|
|
self.__core.onLinphoneCallIncomingReceived = self.on_LinphoneCallIncomingReceived
|
|
|
|
self.__core.onLinphoneCallOutgoingRinging = self.on_LinphoneCallOutgoingRinging
|
|
|
|
self.__core.onLinphoneCallConnected = self.on_LinphoneCallConnected
|
|
|
|
self.__core.onLinphoneCallEnd = self.on_LinphoneCallEnd
|
|
|
|
|
2015-05-28 23:44:35 +02:00
|
|
|
# Create and add all proxy configs
|
|
|
|
for p in config.proxies:
|
2020-12-28 20:56:39 +01:00
|
|
|
aid = self.__core.register(p.identity, p.proxy, p.password, p.username) # sip:XXXX@hg.eventphone.de, hg.eventphone.de, MySecretPassword, XXXX
|
2015-05-18 14:34:45 +02:00
|
|
|
|
2015-05-26 23:11:05 +02:00
|
|
|
self.__audioproc = None
|
2017-04-28 18:57:43 +02:00
|
|
|
aplay = subprocess.Popen(['aplay', '-qD%s' % config.sound_device],
|
|
|
|
stdin=subprocess.PIPE)
|
|
|
|
self.__ttsproc = subprocess.Popen(['espeak', '-p10', '--stdout'],
|
|
|
|
stdin=subprocess.PIPE,
|
|
|
|
stdout=aplay.stdin)
|
2015-05-26 23:11:05 +02:00
|
|
|
|
2021-02-24 23:56:23 +01:00
|
|
|
self.current_call_id = None
|
|
|
|
|
2015-05-18 14:34:45 +02:00
|
|
|
# Set default parameters overriding the ones from the given config file
|
2020-09-03 16:49:13 +02:00
|
|
|
# TODO: figure out how to set at least some of these settings through the unix socket
|
|
|
|
#self.__core.set_user_agent('FeTAp 615', '0.1')
|
|
|
|
#self.__core.stun_server = config.stun_server
|
|
|
|
#self.__core.ringback = ''
|
|
|
|
#self.__core.max_calls = 1
|
|
|
|
#self.__core.inc_timeout = config.incoming_timeout
|
|
|
|
#self.__core.set_call_error_tone(linphone.Reason.Busy, '')
|
|
|
|
#self.__core.disable_chat(linphone.Reason.None)
|
|
|
|
#self.__core.echo_cancellation_enabled = False
|
|
|
|
#self.__core.video_capture_enabled = False
|
|
|
|
#self.__core.video_display_enabled = False
|
2015-05-18 14:34:45 +02:00
|
|
|
|
2020-12-28 21:01:41 +01:00
|
|
|
|
|
|
|
def run_callbacks(self, evt):
|
|
|
|
print(PhoneEvent.string(evt))
|
|
|
|
for cb in self.__event_cbs:
|
|
|
|
cb(evt)
|
|
|
|
|
|
|
|
def on_LinphoneCallIncomingReceived(self, event):
|
|
|
|
self.run_callbacks(PhoneEvent.CallIncoming)
|
2021-02-24 23:56:23 +01:00
|
|
|
self.current_call_id = event.call_id
|
2020-12-28 21:01:41 +01:00
|
|
|
|
|
|
|
def on_LinphoneCallOutgoingRinging(self, event):
|
|
|
|
self.run_callbacks(PhoneEvent.CallRinging)
|
|
|
|
|
|
|
|
def on_LinphoneCallConnected(self, event):
|
|
|
|
self.run_callbacks(PhoneEvent.CallAccepted)
|
|
|
|
|
|
|
|
def on_LinphoneCallEnd(self, event):
|
|
|
|
self.run_callbacks(PhoneEvent.CallEnded)
|
2021-02-24 23:56:23 +01:00
|
|
|
self.current_call_id = None
|
2015-05-28 23:44:35 +02:00
|
|
|
|
2015-05-18 14:34:45 +02:00
|
|
|
def __pollthread(self):
|
|
|
|
while self.__running:
|
2020-09-03 16:49:13 +02:00
|
|
|
self.__core.process_event()
|
|
|
|
time.sleep(0.2) # Value for good measure
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
def start(self):
|
|
|
|
self.__running = True
|
|
|
|
t = threading.Thread(target=self.__pollthread)
|
|
|
|
t.start()
|
|
|
|
|
|
|
|
def stop(self):
|
2015-05-28 23:44:35 +02:00
|
|
|
self.stop_playing()
|
|
|
|
if self.__ttsproc is not None:
|
|
|
|
self.__ttsproc.terminate()
|
2015-05-18 14:34:45 +02:00
|
|
|
self.__running = False
|
|
|
|
|
|
|
|
def add_event_cb(self, cb):
|
|
|
|
self.__event_cbs.append(cb)
|
|
|
|
|
|
|
|
def call(self, number):
|
2020-12-28 21:02:39 +01:00
|
|
|
if '@' not in number:
|
|
|
|
proxy = None
|
|
|
|
default_name = self.__config.default_proxy
|
2015-05-28 23:56:57 +02:00
|
|
|
for p in self.__config.proxies:
|
2020-12-28 21:02:39 +01:00
|
|
|
if p.name == default_name:
|
|
|
|
proxy = p
|
2015-05-28 23:56:57 +02:00
|
|
|
break
|
2020-12-28 21:02:39 +01:00
|
|
|
if proxy is None:
|
|
|
|
# Try to resolve prefix
|
|
|
|
for p in self.__config.proxies:
|
|
|
|
if number.startswith(p.prefix):
|
|
|
|
number = number[len(p.prefix):]
|
|
|
|
proxy = p
|
|
|
|
break
|
|
|
|
if proxy is not None:
|
|
|
|
number += '@' + proxy.realm
|
|
|
|
self.__core.call(number)
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
def accept_call(self):
|
2021-08-29 02:53:01 +02:00
|
|
|
self.__core.answer(self.current_call_id)
|
2015-05-18 14:34:45 +02:00
|
|
|
|
2015-05-19 22:16:07 +02:00
|
|
|
def decline_call(self):
|
2020-12-28 21:02:39 +01:00
|
|
|
self.__core.decline_call(self.__core.current_call)
|
2015-05-19 22:16:07 +02:00
|
|
|
|
2015-05-18 14:34:45 +02:00
|
|
|
def end_call(self):
|
2020-12-28 21:02:39 +01:00
|
|
|
self.__core.terminate()
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
def play_dial_tone(self):
|
2015-05-26 23:11:05 +02:00
|
|
|
self.stop_playing()
|
2017-04-28 18:57:43 +02:00
|
|
|
self.__audioproc = subprocess.Popen(
|
|
|
|
['play', '-nq', 'synth', 'sine', '425'],
|
|
|
|
env={'AUDIODRIVER': 'alsa', 'AUDIODEV': self.__config.sound_device}
|
|
|
|
)
|
2015-05-26 23:11:05 +02:00
|
|
|
|
|
|
|
def play_ringback_tone(self):
|
|
|
|
self.stop_playing()
|
2017-04-28 18:57:43 +02:00
|
|
|
self.__audioproc = subprocess.Popen(
|
|
|
|
['play', '-nq', 'synth', '1', 'sine', '425', 'pad', '4@1',
|
|
|
|
'repeat', '1000'],
|
|
|
|
env={'AUDIODRIVER': 'alsa', 'AUDIODEV': self.__config.sound_device}
|
|
|
|
)
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
def play_busy_tone(self):
|
2015-05-26 23:11:05 +02:00
|
|
|
self.stop_playing()
|
2017-04-28 18:57:43 +02:00
|
|
|
self.__audioproc = subprocess.Popen(
|
|
|
|
['play', '-nq', 'synth', '0.48', 'sine', '425', 'pad', '0.48@0.48',
|
|
|
|
'repeat', '1000'],
|
|
|
|
env={'AUDIODRIVER': 'alsa', 'AUDIODEV': self.__config.sound_device}
|
|
|
|
)
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
def stop_playing(self):
|
2015-05-26 23:11:05 +02:00
|
|
|
if self.__audioproc is not None:
|
|
|
|
self.__audioproc.terminate()
|
2015-05-18 14:34:45 +02:00
|
|
|
|
2015-05-28 23:44:35 +02:00
|
|
|
def read_text(self, text):
|
2020-12-28 00:50:50 +01:00
|
|
|
self.__ttsproc.stdin.write(text.encode('utf8') + b'\n')
|
2015-05-28 23:44:35 +02:00
|
|
|
self.__ttsproc.stdin.flush()
|
|
|
|
|
2015-12-27 11:52:38 +01:00
|
|
|
def get_remote_number(self):
|
2021-02-24 23:56:23 +01:00
|
|
|
if self.current_call_id != None:
|
|
|
|
status = self.__core.call_status(self.current_call_id)
|
|
|
|
return status.get("from")
|
2015-12-27 11:52:38 +01:00
|
|
|
|
2015-05-18 14:34:45 +02:00
|
|
|
if __name__ == '__main__':
|
|
|
|
def event_cb(evt):
|
2020-12-28 00:50:50 +01:00
|
|
|
print('Got event:', PhoneEvent.string(evt))
|
2015-05-18 14:34:45 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
phone = PhoneInterface('.linphonerc-foo', '.linphonerc')
|
|
|
|
phone.add_event_cb(event_cb)
|
|
|
|
phone.start()
|
|
|
|
i = 0
|
|
|
|
while True:
|
|
|
|
time.sleep(1)
|
|
|
|
i += 1
|
|
|
|
if i == 5:
|
|
|
|
phone.call('3474')
|
|
|
|
#phone.play_busy_tone()
|
|
|
|
pass
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
phone.stop()
|
2015-05-28 23:56:57 +02:00
|
|
|
|