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
|
|
|
from time import sleep |
|
|
|
|
9
|
|
|
import linphone |
10
|
|
|
from doorpi import DoorPi |
11
|
|
|
|
12
|
|
|
class LinphoneCallbacks: |
13
|
|
|
|
14
|
|
|
@property |
15
|
|
|
def used_callbacks(self): return { |
16
|
|
|
#http://www.linphone.org/docs/liblinphone/struct__LinphoneCoreVTable.html |
17
|
|
|
#'global_state_changed': self.global_state_changed, #Notifies global state changes |
18
|
|
|
#'registration_state_changed': self.registration_state_changed, #Notifies registration state changes |
19
|
|
|
'call_state_changed': self.call_state_changed, #Notifies call state changes |
20
|
|
|
#'notify_presence_received': self.notify_presence_received, #Notify received presence events |
21
|
|
|
#'new_subscription_requested': self.new_subscription_requested, #Notify about pending presence subscription request |
22
|
|
|
#'auth_info_requested': self.auth_info_requested, #Ask the application some authentication information |
23
|
|
|
#'call_log_updated': self.call_log_updated, #Notifies that call log list has been updated |
24
|
|
|
#'message_received': self.message_received, #a message is received, can be text or external body |
25
|
|
|
#'is_composing_received': self.is_composing_received, #An is-composing notification has been received |
26
|
|
|
'dtmf_received': self.dtmf_received, #A dtmf has been received received |
27
|
|
|
#'refer_received': self.refer_received, #An out of call refer was received |
28
|
|
|
#'call_encryption_changed': self.call_encryption_changed, #Notifies on change in the encryption of call streams |
29
|
|
|
#'transfer_state_changed': self.transfer_state_changed, #Notifies when a transfer is in progress |
30
|
|
|
#'buddy_info_updated': self.buddy_info_updated, #a LinphoneFriend's BuddyInfo has changed |
31
|
|
|
#'call_stats_updated': self.call_stats_updated, #Notifies on refreshing of call's statistics. |
32
|
|
|
#'info_received': self.info_received, #Notifies an incoming informational message received. |
33
|
|
|
#'subscription_state_changed': self.subscription_state_changed, #Notifies subscription state change |
34
|
|
|
#'notify_received': self.notify_received, #Notifies a an event notification, see linphone_core_subscribe() |
35
|
|
|
#'configuring_status': self.configuring_status, #Notifies publish state change (only from LinphoneEvent api) |
36
|
|
|
#'network_reachable': self.network_reachable, #Callback to report IP network status (I.E up/down ) |
37
|
|
|
#'log_collection_upload_state_changed': self.log_collection_upload_state_changed, #Callback to upload collected logs |
38
|
|
|
#'log_collection_upload_progress_indication': self.log_collection_upload_progress_indication #Callback to indicate log collection upload progress |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
@property |
42
|
|
|
def whitelist(self): return DoorPi().config.get_keys('AdminNumbers') |
43
|
|
|
|
44
|
|
|
def is_admin_number(self, remote_uri): |
45
|
|
|
logger.debug("is_admin_number (%s)",remote_uri) |
46
|
|
|
for admin_number in self.whitelist: |
47
|
|
|
if admin_number == "*": |
48
|
|
|
logger.info("admin numbers are deactivated by using '*' as single number") |
49
|
|
|
return True |
50
|
|
|
if "sip:"+admin_number+"@" in remote_uri: |
51
|
|
|
logger.debug("%s is adminnumber %s", remote_uri, admin_number) |
52
|
|
|
return True |
53
|
|
|
if "sip:"+admin_number is remote_uri: |
54
|
|
|
logger.debug("%s is adminnumber %s", remote_uri, admin_number) |
55
|
|
|
return True |
56
|
|
|
logger.debug("%s is not an adminnumber", remote_uri) |
57
|
|
|
return False |
58
|
|
|
|
59
|
|
|
__DTMF = '' |
60
|
|
|
__possible_DTMF = [] |
61
|
|
|
|
62
|
|
|
def __init__(self): |
63
|
|
|
logger.debug("__init__") |
64
|
|
|
|
65
|
|
|
self._last_number_of_calls = 0 |
66
|
|
|
|
67
|
|
|
DoorPi().event_handler.register_action('OnSipPhoneDestroy', self.destroy) |
68
|
|
|
|
69
|
|
|
DoorPi().event_handler.register_event('OnCallMediaStateChange', __name__) |
70
|
|
|
DoorPi().event_handler.register_event('OnMediaRequired', __name__) |
71
|
|
|
DoorPi().event_handler.register_event('OnMediaNotRequired', __name__) |
72
|
|
|
|
73
|
|
|
DoorPi().event_handler.register_event('OnCallStateChange', __name__) |
74
|
|
|
DoorPi().event_handler.register_event('OnCallStateConnect', __name__) |
75
|
|
|
DoorPi().event_handler.register_event('AfterCallStateConnect', __name__) |
76
|
|
|
DoorPi().event_handler.register_event('OnCallStateDisconnect', __name__) |
77
|
|
|
DoorPi().event_handler.register_event('AfterCallStateDisconnect', __name__) |
78
|
|
|
DoorPi().event_handler.register_event('OnCallStateDismissed', __name__) |
79
|
|
|
DoorPi().event_handler.register_event('OnCallStateReject', __name__) |
80
|
|
|
DoorPi().event_handler.register_event('OnCallStart', __name__) |
81
|
|
|
DoorPi().event_handler.register_event('OnDTMF', __name__) |
82
|
|
|
|
83
|
|
|
self.__possible_DTMF = DoorPi().config.get_keys('DTMF') |
84
|
|
|
for DTMF in self.__possible_DTMF: |
85
|
|
|
DoorPi().event_handler.register_event('OnDTMF_'+DTMF, __name__) |
86
|
|
|
|
87
|
|
|
DoorPi().event_handler.register_event('OnCallStart', __name__) |
88
|
|
|
DoorPi().event_handler.register_event('BeforeCallIncoming', __name__) |
89
|
|
|
DoorPi().event_handler.register_event('OnCallReconnect', __name__) |
90
|
|
|
DoorPi().event_handler.register_event('AfterCallReconnect', __name__) |
91
|
|
|
DoorPi().event_handler.register_event('OnCallBusy', __name__) |
92
|
|
|
DoorPi().event_handler.register_event('AfterCallBusy', __name__) |
93
|
|
|
DoorPi().event_handler.register_event('OnCallIncoming', __name__) |
94
|
|
|
DoorPi().event_handler.register_event('AfterCallIncoming', __name__) |
95
|
|
|
DoorPi().event_handler.register_event('OnCallReject', __name__) |
96
|
|
|
DoorPi().event_handler.register_event('AfterCallReject', __name__) |
97
|
|
|
#DoorPi().event_handler.register_event('AfterAccountRegState', __name__) |
98
|
|
|
|
99
|
|
|
DoorPi().event_handler('OnCallStart', __name__) |
100
|
|
|
|
101
|
|
|
def destroy(self): |
102
|
|
|
logger.debug("destroy") |
103
|
|
|
DoorPi().event_handler.unregister_source(__name__, True) |
104
|
|
|
|
105
|
|
|
def global_state_changed(self, core, global_state, message): pass |
106
|
|
|
def registration_state_changed(self, core, linphone_proxy_config, state, message): pass |
107
|
|
|
def call_state_changed(self, core, call, call_state, message): |
108
|
|
|
self.call_state_changed_handle(core, call, call_state, message) |
109
|
|
|
|
110
|
|
|
if core.calls_nb > 0 and self._last_number_of_calls == 0: |
111
|
|
|
DoorPi().event_handler('OnMediaRequired', __name__) |
112
|
|
|
elif self._last_number_of_calls is not core.calls_nb: |
113
|
|
|
DoorPi().event_handler('OnMediaNotRequired', __name__) |
114
|
|
|
self._last_number_of_calls = core.calls_nb |
115
|
|
|
|
116
|
|
|
def call_state_changed_handle(self, core, call, call_state, message): |
117
|
|
|
logger.debug("call_state_changed (%s - %s)", call_state, message) |
118
|
|
|
|
119
|
|
|
remote_uri = call.remote_address.as_string_uri_only() |
120
|
|
|
|
121
|
|
|
DoorPi().event_handler('OnCallStateChange', __name__, { |
122
|
|
|
'remote_uri': remote_uri, |
123
|
|
|
'call_state': call_state, |
124
|
|
|
'state': message |
125
|
|
|
}) |
126
|
|
|
|
127
|
|
|
if call_state == linphone.CallState.Idle: |
128
|
|
|
pass |
129
|
|
|
elif call_state == linphone.CallState.IncomingReceived: |
130
|
|
|
DoorPi().event_handler('BeforeCallIncoming', __name__, {'remote_uri': remote_uri}) |
131
|
|
|
if core.current_call and core.current_call.state > linphone.CallState.IncomingReceived: |
132
|
|
|
logger.debug("Incoming call while another call is active") |
133
|
|
|
logger.debug("- incoming.remote_uri: %s", call) |
134
|
|
|
logger.debug("- current.remote_uri : %s", core.current_call) |
135
|
|
|
|
136
|
|
|
if core.current_call.remote_address.as_string_uri_only() == remote_uri: |
137
|
|
|
logger.info("Current call is incoming call - quitting current and connecting to incoming. Maybe connection reset?") |
138
|
|
|
DoorPi().event_handler('OnCallReconnect', __name__, {'remote_uri': remote_uri}) |
139
|
|
|
core.terminate_call(core.current_call) |
140
|
|
|
DoorPi().sipphone.reset_call_start_datetime() |
141
|
|
|
core.accept_call_with_params(call, DoorPi().sipphone.base_config) |
142
|
|
|
DoorPi().event_handler('AfterCallReconnect', __name__) |
143
|
|
|
return |
144
|
|
|
else: |
145
|
|
|
if self.is_admin_number(remote_uri): |
146
|
|
|
logger.info("Incoming and current call are different - incoming is AdminNumber, so hanging up current call") |
147
|
|
|
DoorPi().event_handler('OnCallIncoming', __name__, {'remote_uri': remote_uri}) |
148
|
|
|
core.terminate_call(core.current_call) |
149
|
|
|
DoorPi().sipphone.reset_call_start_datetime() |
150
|
|
|
core.accept_call_with_params(call, DoorPi().sipphone.base_config) |
151
|
|
|
DoorPi().event_handler('AfterCallIncoming', __name__, {'remote_uri': remote_uri}) |
152
|
|
|
return |
153
|
|
|
else: |
154
|
|
|
logger.info("Incoming and current call are different - sending busy signal to incoming call") |
155
|
|
|
DoorPi().event_handler('OnCallBusy', __name__, {'remote_uri': remote_uri}) |
156
|
|
|
core.decline_call(call, linphone.Reason.Busy) |
157
|
|
|
DoorPi().event_handler('AfterCallBusy', __name__) |
158
|
|
|
return |
159
|
|
|
if self.is_admin_number(remote_uri): |
160
|
|
|
DoorPi().event_handler('OnCallIncoming', __name__, {'remote_uri': remote_uri}) |
161
|
|
|
DoorPi().sipphone.reset_call_start_datetime() |
162
|
|
|
core.accept_call_with_params(call, DoorPi().sipphone.base_config) |
163
|
|
|
DoorPi().event_handler('AfterCallIncoming', __name__, {'remote_uri': remote_uri}) |
164
|
|
|
return |
165
|
|
|
else: |
166
|
|
|
DoorPi().event_handler('OnCallReject', __name__) |
167
|
|
|
core.decline_call(call, linphone.Reason.Forbidden) #Declined |
168
|
|
|
DoorPi().event_handler('AfterCallReject', __name__) |
169
|
|
|
return |
170
|
|
|
elif call_state == linphone.CallState.OutgoingInit: |
171
|
|
|
pass |
172
|
|
|
elif call_state == linphone.CallState.OutgoingProgress: |
173
|
|
|
pass |
174
|
|
|
elif call_state == linphone.CallState.OutgoingRinging: |
175
|
|
|
pass |
176
|
|
|
elif call_state == linphone.CallState.OutgoingEarlyMedia: |
177
|
|
|
DoorPi().event_handler('OnCallMediaStateChange', __name__) |
178
|
|
|
elif call_state == linphone.CallState.Connected: |
179
|
|
|
DoorPi().event_handler('OnCallStateConnect', __name__) |
180
|
|
|
elif call_state == linphone.CallState.StreamsRunning: |
181
|
|
|
DoorPi().event_handler('AfterCallStateConnect', __name__) |
182
|
|
|
DoorPi().event_handler('OnCallMediaStateChange', __name__) |
183
|
|
|
elif call_state == linphone.CallState.Pausing: |
184
|
|
|
pass |
185
|
|
|
elif call_state == linphone.CallState.Paused: |
186
|
|
|
DoorPi().event_handler('OnCallMediaStateChange', __name__) |
187
|
|
|
elif call_state == linphone.CallState.Resuming: |
188
|
|
|
DoorPi().event_handler('OnCallStateConnect', __name__) |
189
|
|
|
DoorPi().event_handler('OnCallMediaStateChange', __name__) |
190
|
|
|
elif call_state == linphone.CallState.Refered: |
191
|
|
|
pass |
192
|
|
|
elif call_state == linphone.CallState.Error: |
193
|
|
|
if message == "Busy here": DoorPi().event_handler('OnCallStateDismissed', __name__) |
194
|
|
|
elif call_state == linphone.CallState.End: |
195
|
|
|
if message == "Call declined.": DoorPi().event_handler('OnCallStateReject', __name__) |
196
|
|
|
DoorPi().event_handler('OnCallStateDisconnect', __name__) |
197
|
|
|
elif call_state == linphone.CallState.PausedByRemote: |
198
|
|
|
pass |
199
|
|
|
elif call_state == linphone.CallState.UpdatedByRemote: |
200
|
|
|
pass |
201
|
|
|
elif call_state == linphone.CallState.IncomingEarlyMedia: |
202
|
|
|
DoorPi().event_handler('OnCallMediaStateChange', __name__) |
203
|
|
|
elif call_state == linphone.CallState.Updating: |
204
|
|
|
DoorPi().event_handler('OnCallStateConnect', __name__) |
205
|
|
|
DoorPi().event_handler('OnCallMediaStateChange', __name__) |
206
|
|
|
elif call_state == linphone.CallState.Released: |
207
|
|
|
pass |
208
|
|
|
elif call_state == linphone.CallState.EarlyUpdatedByRemote: |
209
|
|
|
pass |
210
|
|
|
elif call_state == linphone.CallState.EarlyUpdating: |
211
|
|
|
pass |
212
|
|
|
def notify_presence_received(self, core, linphone_friend): pass |
213
|
|
|
def new_subscription_requested(self, core, linphone_friend, url): pass |
214
|
|
|
def auth_info_requested(self, core, realm, username): pass |
215
|
|
|
def call_log_updated(self, core, new_call_log_entry): pass |
216
|
|
|
def message_received(self, core, linphone_chat_room, message): pass |
217
|
|
|
def is_composing_received(self, core, linphone_chat_room): pass |
218
|
|
|
def dtmf_received(self, core, call, digits): |
|
|
|
|
219
|
|
|
logger.debug("on_dtmf_digit (%s)", str(digits)) |
220
|
|
|
digits = chr(digits) |
221
|
|
|
DoorPi().event_handler('OnDTMF', __name__, {'digits':digits}) |
222
|
|
|
self.__DTMF += str(digits) |
223
|
|
|
for DTMF in self.__possible_DTMF: |
224
|
|
|
if self.__DTMF.endswith(DTMF[1:-1]): |
225
|
|
|
DoorPi().event_handler('OnDTMF_'+DTMF+'', __name__, { |
226
|
|
|
'remote_uri': str(call.remote_address.as_string_uri_only()), |
227
|
|
|
'DTMF': str(self.__DTMF) |
228
|
|
|
}) |
229
|
|
|
def refer_received(self, core, refer_to): pass |
230
|
|
|
def call_encryption_changed(self, core, call, on, authentication_token): pass |
231
|
|
|
def transfer_state_changed(self, core, call, transfer_state): pass |
232
|
|
|
def buddy_info_updated(self, core, linphone_friend): pass |
233
|
|
|
def call_stats_updated(self, core, call, stats): pass |
234
|
|
|
def info_received(self, core, call, message): pass |
235
|
|
|
def subscription_state_changed(self, core, linphone_event, linphone_subscription_state): pass |
236
|
|
|
def notify_received(self, core, linphone_event, linphone_subscription_state, linphone_body): pass |
237
|
|
|
def configuring_status(self, core, linphone_configuring_state, message): pass |
238
|
|
|
def network_reachable(self, core, reachable): pass |
239
|
|
|
def log_collection_upload_state_changed(self, core, linphone_core_log_collection_upload_state, info): pass |
240
|
|
|
def log_collection_upload_progress_indication(self, core, offset, total): pass |
241
|
|
|
|
242
|
|
|
__del__ = destroy |