81 lines
3.2 KiB
Python
81 lines
3.2 KiB
Python
import weakref
|
|
|
|
from gsmmodem.exceptions import CmeError, InterruptedException, InvalidStateException
|
|
|
|
|
|
class Call(object):
|
|
""" A voice call """
|
|
|
|
DTMF_COMMAND_BASE = '+VTS='
|
|
dtmfSupport = False # Indicates whether or not DTMF tones can be sent in calls
|
|
|
|
def __init__(self, gsmModem, callId, callType, number, callStatusUpdateCallbackFunc=None):
|
|
"""
|
|
:param gsmModem: GsmModem instance that created this object
|
|
:param number: The number that is being called
|
|
"""
|
|
self._gsmModem = weakref.proxy(gsmModem)
|
|
self._callStatusUpdateCallbackFunc = callStatusUpdateCallbackFunc
|
|
# Unique ID of this call
|
|
self.id = callId
|
|
# Call type (VOICE == 0, etc)
|
|
self.type = callType
|
|
# The remote number of this call (destination or origin)
|
|
self.number = number
|
|
# Flag indicating whether the call has been answered or not (backing field for "answered" property)
|
|
self._answered = False
|
|
# Flag indicating whether or not the call is active
|
|
# (meaning it may be ringing or answered, but not ended because of a hangup event)
|
|
self.active = True
|
|
|
|
@property
|
|
def answered(self):
|
|
return self._answered
|
|
|
|
@answered.setter
|
|
def answered(self, answered):
|
|
self._answered = answered
|
|
if self._callStatusUpdateCallbackFunc:
|
|
self._callStatusUpdateCallbackFunc(self)
|
|
|
|
def sendDtmfTone(self, tones):
|
|
""" Send one or more DTMF tones to the remote party (only allowed for an answered call)
|
|
|
|
Note: this is highly device-dependent, and might not work
|
|
|
|
:param digits: A str containining one or more DTMF tones to play, e.g. "3" or "\*123#"
|
|
|
|
:raise CommandError: if the command failed/is not supported
|
|
:raise InvalidStateException: if the call has not been answered, or is ended while the command is still executing
|
|
"""
|
|
if self.answered:
|
|
dtmfCommandBase = self.DTMF_COMMAND_BASE.format(cid=self.id)
|
|
toneLen = len(tones)
|
|
for tone in list(tones):
|
|
try:
|
|
self._gsmModem.write('AT{0}{1}'.format(dtmfCommandBase, tone), timeout=(5 + toneLen))
|
|
|
|
except CmeError as e:
|
|
if e.code == 30:
|
|
# No network service - can happen if call is ended during DTMF transmission (but also if DTMF is sent immediately after call is answered)
|
|
raise InterruptedException('No network service', e)
|
|
elif e.code == 3:
|
|
# Operation not allowed - can happen if call is ended during DTMF transmission
|
|
raise InterruptedException('Operation not allowed', e)
|
|
else:
|
|
raise e
|
|
else:
|
|
raise InvalidStateException('Call is not active (it has not yet been answered, or it has ended).')
|
|
|
|
def hangup(self):
|
|
""" End the phone call.
|
|
|
|
Does nothing if the call is already inactive.
|
|
"""
|
|
if self.active:
|
|
self._gsmModem.write('ATH')
|
|
self.answered = False
|
|
self.active = False
|
|
if self.id in self._gsmModem.activeCalls:
|
|
del self._gsmModem.activeCalls[self.id]
|