Issues (158)

doorpi/sipphone/from_linphone.py (5 issues)

Checks for unused arguments.

Unused Code Minor
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
import logging
5
logger = logging.getLogger(__name__)
6
logger.debug("%s loaded", __name__)
7
8
import datetime
9
10
from AbstractBaseClass import SipphoneAbstractBaseClass, SIPPHONE_SECTION
11
import linphone as lin
12
13
from doorpi import DoorPi
14
from doorpi.sipphone.linphone_lib.CallBacks import LinphoneCallbacks
15
from doorpi.sipphone.linphone_lib.Player import LinphonePlayer
16
from doorpi.sipphone.linphone_lib.Recorder import LinphoneRecorder
17
from doorpi.media.CreateDialTone import generate_dial_tone
18
19
conf = DoorPi().config
20
21
def log_handler(level, msg):
22
    if "pylinphone_Core_instance_method_iterate" in msg: return
23
    if "pylinphone_Core_get_current_call" in msg: return
24
    if "pylinphone_Call_from_native_ptr" in msg: return
25
    if ": keep alive sent to [" in msg: return
26
    method = getattr(logger, level)
27
    method(msg)
28
29
if logger.getEffectiveLevel() <= 5: lin.set_log_handler(log_handler)
30
31
def get(*args, **kwargs): return LinPhone(*args, **kwargs)
32
class LinPhone(SipphoneAbstractBaseClass):
33
34
    @property
35
    def name(self): return 'linphone wrapper'
36
37
    @property
38
    def lib(self): return self.__Lib
39
    @property
40
    def core(self): return self.__Lib
41
42
    @property
43
    def recorder(self): return self.__recorder
44
    __recorder = None
45
46
    @property
47
    def player(self): return self.__player
48
    __player = None
49
50
    @property
51
    def current_call(self): return self.core.current_call
52
53
    @property
54
    def video_devices(self):
55
        try:
56
            all_devices = []
57
            for video_device in self.core.video_devices:
58
                all_devices.append({
59
                  'name':       video_device
60
                })
61
            return all_devices
62
        except Exception:
63
            return []
64
65
    @property
66
    def sound_devices(self):
67
        try:
68
            all_devices = []
69
            for sound_device in self.core.sound_devices:
70
                all_devices.append({
71
                  'name':       sound_device,
72
                  'capture':    self.core.sound_device_can_capture(sound_device),
73
                  'record':     self.core.sound_device_can_playback(sound_device)
74
                })
75
            return all_devices
76
        except Exception as exp:
77
            logger.exception(exp)
78
            return []
79
80
    def _create_payload_enum(self, payloads):
81
82
        try:
83
            all_codecs = []
84
            for codec in payloads:
85
                all_codecs.append({
86
                    'name':         codec.mime_type,
87
                    'channels':     codec.channels,
88
                    'bitrate':      codec.normal_bitrate,
89
                    'enable':       self.core.payload_type_enabled(codec)
90
                })
91
            return all_codecs
92
        except Exception as exp:
93
            logger.exception(exp)
94
            return []
95
96
    @property
97
    def video_codecs(self):
98
        return self._create_payload_enum(self.core.video_codecs)
99
100
    @property
101
    def sound_codecs(self):
102
        return self._create_payload_enum(self.core.audio_codecs)
103
104
    @property
105
    def current_call_duration(self):
106
        if not self.current_call: return 0
107
        diff_start_and_now = datetime.datetime.utcnow() - self.__current_call_start_datetime
108
        return diff_start_and_now.total_seconds()
109
110
    @property
111
    def current_call_dump(self):
112
        try:
113
            return {
114
                'direction':        'incoming' if self.current_call.dir == 0 else 'outgoing',
115
                'remote_uri':       self.current_call.remote_address_as_string,
116
                'total_time':       self.current_call_duration,
117
                'level_incoming':   self.current_call.record_volume,
118
                'level_outgoing':   self.current_call.play_volume,
119
                'camera':           self.current_call.camera_enabled
120
            }
121
        except Exception:
122
            return {}
123
124
    #TODO: Datetime from linphone CallLog.start_date is more then 30 sec different to python datetime.utcnow()?
125
    __current_call_start_datetime = datetime.datetime.utcnow()
126
127
    @property
128
    def base_config(self):
129
        params = self.core.create_call_params(None)
130
        params.record_file = self.recorder.reset_last_record_filename()
131
        params.video_enabled = True
132
        return params
133
134
    def reset_call_start_datetime(self):
135
        self.__current_call_start_datetime = datetime.datetime.utcnow()
136
        logger.debug('reset current call start datetime to %s', self.__current_call_start_datetime)
137
        return self.__current_call_start_datetime
138
139
    def __init__(self, whitelist = list(), *args, **kwargs):
0 ignored issues
show
The argument kwargs seems to be unused.
Loading history...
The argument whitelist seems to be unused.
Loading history...
The argument args seems to be unused.
Loading history...
140
        logger.debug("__init__")
141
142
        DoorPi().event_handler.register_action('OnShutdown', self.destroy)
143
144
        DoorPi().event_handler.register_event('OnSipPhoneCreate', __name__)
145
        DoorPi().event_handler.register_event('OnSipPhoneStart', __name__)
146
        DoorPi().event_handler.register_event('OnSipPhoneDestroy', __name__)
147
148
        DoorPi().event_handler.register_event('OnSipPhoneRecorderCreate', __name__)
149
        DoorPi().event_handler.register_event('OnSipPhoneRecorderDestroy', __name__)
150
151
        DoorPi().event_handler.register_event('BeforeSipPhoneMakeCall', __name__)
152
        DoorPi().event_handler.register_event('OnSipPhoneMakeCall', __name__)
153
        DoorPi().event_handler.register_event('OnSipPhoneMakeCallFailed', __name__)
154
        DoorPi().event_handler.register_event('AfterSipPhoneMakeCall', __name__)
155
        
156
        DoorPi().event_handler.register_event('OnSipPhoneCallTimeoutNoResponse', __name__)
157
        DoorPi().event_handler.register_event('OnSipPhoneCallTimeoutMaxCalltime', __name__)
158
159
        DoorPi().event_handler.register_event('OnPlayerCreated', __name__)
160
161
        #http://pythonhosted.org/linphone/api_reference.html#linphone.Core.new
162
        self.callback = LinphoneCallbacks()
163
        config_path = None
164
        factory_config_path = None
165
        self.__Lib = lin.Core.new(
166
            self.callback.used_callbacks,
167
            config_path,
168
            factory_config_path
169
        )
170
        self.core.primary_contact = '%s <sip:[email protected]>'%conf.get(SIPPHONE_SECTION, "identity", 'DoorPi')
171
172
    def start(self):
173
        DoorPi().event_handler('OnSipPhoneCreate', __name__)
174
        self.core.max_calls = conf.get_int(SIPPHONE_SECTION, 'ua.max_calls', 2)
175
        self.core.echo_cancellation_enabled = conf.get_bool(SIPPHONE_SECTION, 'echo_cancellation_enabled', False)
176
        
177
        # set local listen ports, default: random
178
        self.core.sip_transports = lin.SipTransports(conf.get_int(SIPPHONE_SECTION, 'local_port', 5060), conf.get_int(SIPPHONE_SECTION, 'local_port', 5060), -1, -1)
179
180
        self.core.video_display_enabled = conf.get_bool(SIPPHONE_SECTION, 'video_display_enabled', False)
181
        self.core.stun_server = conf.get(SIPPHONE_SECTION, 'stun_server', '')
182
        firewall_policy = conf.get(SIPPHONE_SECTION, 'FirewallPolicy', 'PolicyNoFirewall')
183
        if firewall_policy == "PolicyNoFirewall": self.core.firewall_policy = lin.FirewallPolicy.PolicyNoFirewall
184
        elif firewall_policy == "PolicyUseNatAddress": self.core.firewall_policy = lin.FirewallPolicy.PolicyUseNatAddress
185
        elif firewall_policy == "PolicyUseStun": self.core.firewall_policy = lin.FirewallPolicy.PolicyUseStun
186
        elif firewall_policy == "PolicyUseIce": self.core.firewall_policy = lin.FirewallPolicy.PolicyUseIce
187
        elif firewall_policy == "PolicyUseUpnp": self.core.firewall_policy = lin.FirewallPolicy.PolicyUseUpnp
188
        else: self.core.firewall_policy = lin.FirewallPolicy.PolicyNoFirewall
189
190
        #http://pythonhosted.org/linphone/api_reference.html#linphone.Core.in_call_timeout
191
        #After this timeout period, the call is automatically hangup.
192
        self.core.in_call_timeout = conf.get_int(SIPPHONE_SECTION, 'max_call_time', 120)
193
        #http://pythonhosted.org/linphone/api_reference.html#linphone.Core.inc_timeout
194
        #If an incoming call isn’t answered for this timeout period, it is automatically declined.
195
        self.core.inc_timeout = conf.get_int(SIPPHONE_SECTION, 'call_timeout', 15)
196
197
        self.__player = LinphonePlayer()
198
        self.core.ringback = self.player.player_filename
199
        self.__recorder = LinphoneRecorder()
200
201
        if len(self.core.sound_devices) == 0:
202
            logger.warning('no audio devices available')
203
        else:
204
            self.core.capture_device = conf.get(SIPPHONE_SECTION, 'capture_device', self.core.capture_device)
205
            self.core.playback_device = conf.get(SIPPHONE_SECTION, 'playback_device', self.core.playback_device)
206
            self.core.mic_gain_db = conf.get_float(SIPPHONE_SECTION, 'mic_gain_db', 0)
207
            logger.info("found %s possible sounddevices:", len(self.core.sound_devices))
208
            logger.debug("|rec|play| name")
209
            logger.debug("------------------------------------")
210
            for sound_device in self.core.sound_devices:
211
                logger.debug("| %s | %s  | %s",
212
                    'X' if self.core.sound_device_can_capture(sound_device) else 'O',
213
                    'X' if self.core.sound_device_can_playback(sound_device) else 'O',
214
                    sound_device
215
                )
216
            logger.debug("------------------------------------")
217
            logger.debug("using capture_device: %s", self.core.capture_device)
218
            logger.debug("using playback_device: %s", self.core.playback_device)
219
            logger.debug("mic_gain_db: %s", self.core.mic_gain_db)
220
221
        # Only enable PCMU and PCMA audio codecs by default
222
        config_audio_codecs = conf.get_list(SIPPHONE_SECTION, 'audio_codecs', 'PCMA,PCMU')
223
        for codec in self.core.audio_codecs:
224
            if codec.mime_type in config_audio_codecs:
225
                logger.debug('enable audio codec %s', codec.mime_type)
226
                self.core.enable_payload_type(codec, True)
227
            else:
228
                logger.debug('disable audio codec %s', codec.mime_type)
229
                self.core.enable_payload_type(codec, False)
230
231
232
        if len(self.core.video_devices) == 0:
233
            self.core.video_capture_enabled = False
234
            logger.warning('no video devices available')
235
        else:
236
            logger.info("found %s possible videodevices:", len(self.core.video_devices))
237
            logger.debug("| name")
238
            logger.debug("------------------------------------")
239
            for video_device in self.core.video_devices:
240
                logger.debug("| %s ", video_device)
241
            logger.debug("------------------------------------")
242
            config_camera = conf.get(SIPPHONE_SECTION, 'video_device', self.core.video_devices[0])
243
            if config_camera not in self.core.video_devices:
244
                logger.warning('camera "%s" from config does not exist in possible video devices.', config_camera)
245
                logger.debug('switching to first possible video device "%s"', self.core.video_devices[0])
246
                config_camera = self.core.video_devices[0]
247
248
            self.core.video_capture_enabled = True
249
            self.core.video_device = config_camera
250
            self.core.preferred_video_size_by_name = conf.get(SIPPHONE_SECTION, 'video_size', 'vga')
251
            logger.debug("using video_device: %s", self.core.video_device)
252
253
        # Only enable VP8 video codec
254
        config_video_codecs = conf.get_list(SIPPHONE_SECTION, 'video_codecs', 'VP8')
255
        for codec in self.core.video_codecs:
256
            if codec.mime_type in config_video_codecs and self.core.video_capture_enabled:
257
                logger.debug('enable video codec %s', codec.mime_type)
258
                self.core.enable_payload_type(codec, True)
259
            else:
260
                logger.debug('disable video codec %s', codec.mime_type)
261
                self.core.enable_payload_type(codec, False)
262
263
        # Configure the SIP account
264
        server = conf.get(SIPPHONE_SECTION, "sipserver_server")
265
        username = conf.get(SIPPHONE_SECTION, "sipserver_username")
266
        password = conf.get(SIPPHONE_SECTION, "sipserver_password", username)
267
        realm = conf.get(SIPPHONE_SECTION, "sipserver_realm", server)
268
        if server and username and password:
269
            logger.info('using DoorPi with SIP-Server')
270
            proxy_cfg = self.core.create_proxy_config()
271
            proxy_cfg.identity_address = lin.Address.new("%s <sip:%[email protected]%s>" % (
272
                    conf.get(SIPPHONE_SECTION, "identity", 'DoorPi'), username, server)
273
            )
274
            proxy_cfg.server_addr = "sip:%s"%server
275
            proxy_cfg.register_enabled = True
276
            self.core.add_proxy_config(proxy_cfg)
277
            self.core.default_proxy_config = proxy_cfg
278
            auth_info = self.core.create_auth_info(username, None, password, None, None, realm)
279
            self.core.add_auth_info(auth_info)
280
        else:
281
            logger.info('using DoorPi without SIP-Server? Okay...')
282
            proxy_cfg = self.core.create_proxy_config()
283
            proxy_cfg.register_enabled = False
284
            self.core.add_proxy_config(proxy_cfg)
285
            self.core.default_proxy_config = proxy_cfg
286
            logger.debug('%s',self.core.proxy_config_list)
287
288
        logger.debug("start successfully")
289
290
    def destroy(self):
291
        logger.debug("destroy")
292
        self.core.terminate_all_calls()
293
        DoorPi().event_handler.fire_event_synchron('OnSipPhoneDestroy', __name__)
294
        DoorPi().event_handler.unregister_source(__name__, True)
295
        return
296
297
    def self_check(self, *args, **kwargs):
0 ignored issues
show
The argument kwargs seems to be unused.
Loading history...
The argument args seems to be unused.
Loading history...
298
        if not self.core: return
299
300
        self.core.iterate()
301
302
        if not self.current_call: return
303
304
        if self.current_call.state < lin.CallState.Connected:
305
            if self.current_call_duration > self.core.inc_timeout - 0.5:
306
                logger.info("call timeout - hangup current call after %s seconds (max. %s)", self.current_call_duration, self.core.inc_timeout)
307
                self.core.terminate_all_calls()
308
                DoorPi().event_handler('OnSipPhoneCallTimeoutNoResponse', __name__)
309
        else:
310
            if int(self.current_call_duration) > self.core.in_call_timeout - 0.5:
311
                logger.info("max call time reached - hangup current call after %s seconds (max. %s)", self.current_call_duration, self.core.in_call_timeout)
312
                self.core.terminate_all_calls()
313
                DoorPi().event_handler('OnSipPhoneCallTimeoutMaxCalltime', __name__)
314
315
    def call(self, number):
316
        DoorPi().event_handler('BeforeSipPhoneMakeCall', __name__, {'number':number})
317
        logger.debug("call (%s)",str(number))
318
        if not self.current_call:
319
            logger.debug('no current call -> start new call')
320
            self.reset_call_start_datetime()
321
            if self.core.invite_with_params(number, self.base_config) is None:
322
                if DoorPi().event_handler.db.get_event_log_entries_count('OnSipPhoneMakeCallFailed') > 5:
323
                    logger.error('failed to execute call five times')
324
                else:
325
                    DoorPi().event_handler('OnSipPhoneMakeCallFailed', __name__, {'number':number})
326
                return None
327
            DoorPi().event_handler('OnSipPhoneMakeCall', __name__, {'number':number})
328
        elif number in self.current_call.remote_address.as_string_uri_only():
329
            if self.current_call_duration <= 2:
330
                logger.debug("same call %s again while call is running since %s seconds? -> skip",
331
                             self.core.current_call.remote_address.as_string_uri_only(),
332
                             self.current_call_duration
333
                )
334
            else:
335
                logger.debug("press twice with call duration > 1 second? Want to hangup current call? OK...")
336
                self.core.terminate_all_calls()
337
        else:
338
            logger.debug("new call needed? hangup old first...")
339
            self.core.terminate_all_calls()
340
            self.call(number)
341
342
        DoorPi().event_handler('AfterSipPhoneMakeCall', __name__, {'number':number})
343
        return self.current_call
344
345
    def is_admin_number(self, remote_uri):
346
        return self.callback.is_admin_number(remote_uri)
347
348
    def hangup(self):
349
        if self.current_call:
350
            logger.debug("Received hangup request, cancelling current call")
351
            self.core.terminate_call(self.current_call)
352
        else:
353
            logger.debug("Ignoring hangup request as there is no ongoing call")
354
355