453 lines
16 KiB
Python
453 lines
16 KiB
Python
"""capisuite.core
|
|
|
|
This module exposes the built-in core of capisuite.
|
|
"""
|
|
|
|
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
|
|
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
|
|
__version__ = "$Revision: 0.0 $"
|
|
__credits__ = "This file is part of www.capisuite.de; thanks to Gernot Hillier"
|
|
__license__ = """
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
"""
|
|
|
|
|
|
# _capisuite may only be imported when running within capisuite
|
|
try:
|
|
# import all capisuite symbols in the namespace "_capisuite.symbol"
|
|
import _capisuite
|
|
# now add symbols directly used by the scripts to our namespace
|
|
from _capisuite import log,error,SERVICE_VOICE,SERVICE_FAXG3,CallGoneError
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
#########
|
|
###
|
|
### ATTENTION: This interface is not yet stable. You may expect
|
|
### changes until capisuite 0.5 is released!
|
|
###
|
|
#########
|
|
|
|
class Capi:
|
|
def __init__(self, handle):
|
|
"""
|
|
handle: a capi handle as received from _capisuite (given to the idle
|
|
function as parameter)
|
|
"""
|
|
self._handle = handle
|
|
|
|
def __repr__(self):
|
|
return 'Capi(%(_handle)s)' % self.__dict__
|
|
|
|
def call_voice(self, controller, call_from, call_to,
|
|
timeout, clir=0):
|
|
"""
|
|
Initiate an outgoing call with service voice and wait for
|
|
successful connection.
|
|
|
|
This will initiate an outgoing call and choose voice as
|
|
service, so you can use the audio commands (like audio_receive()
|
|
and audio_send()) with this connection. After this command has
|
|
finished, the call is connected successfully or the given
|
|
timeout has exceeded. The timeout is measured beginning at the
|
|
moment when the call is signalled (it's "ringing") to the
|
|
called party.
|
|
|
|
Parameters:
|
|
controller: ISDN controller ID to use
|
|
call_from: own number to use (string)
|
|
call_to: the number to call (string)
|
|
timeout: timeout in seconds to wait for connection establishment
|
|
clir: disable sending of own number (default=0, send number)
|
|
|
|
On success returns a call object; on failure returns an
|
|
error_code.
|
|
"""
|
|
call, result = _capisuite.call_voice(self._handle, controller,
|
|
call_from, call_to,
|
|
timeout, clir)
|
|
if result:
|
|
return None, result
|
|
return Call(call, SERVICE_VOICE, call_from, call_to), None
|
|
|
|
|
|
def call_faxG3(self, controller, call_from, call_to,
|
|
timeout, stationID, headline, clir=0):
|
|
"""
|
|
Initiate an outgoing call with service faxG3 and wait for
|
|
successful connection.
|
|
|
|
This will initiate an outgoing call and choose fax group 3 as
|
|
service, so you can use the fax commands (like fax_send() and
|
|
fax_receive()) with this connection. After this command has
|
|
finished, the call is connected successfully or the given
|
|
timeout has exceeded. The timeout is measured beginning at the
|
|
moment when the call is signalled (it's "ringing") to the
|
|
called party.
|
|
|
|
Parameters:
|
|
controller: ISDN controller ID to use
|
|
call_from: own number to use (string)
|
|
call_to: the number to call (string)
|
|
timeout: timeout in seconds to wait for connection establishment
|
|
faxStationID: fax station ID (string)
|
|
faxHeadline: fax headline to print on every page (string)
|
|
clir: disable sending of own number (default=0, send number)
|
|
|
|
On success returns a call object; on failure returns an
|
|
error_code.
|
|
"""
|
|
call, result = _capisuite.call_faxG3(self._handle, controller,
|
|
call_from, call_to,
|
|
timeout, stationID, headline)
|
|
if result:
|
|
return None, result
|
|
return Call(call, SERVICE_FAXG3, call_from, call_to), None
|
|
|
|
|
|
class Call:
|
|
def __init__(self, handle, service, call_from, call_to):
|
|
"""
|
|
handle: a call handle as received from _capisuite
|
|
|
|
NB: A Call instance is never True to ease testing results from
|
|
Capi.call_...()
|
|
"""
|
|
self._handle = handle
|
|
self.service = service
|
|
self.from_nr = call_from
|
|
self.to_nr = call_to
|
|
|
|
###--- python stuff --###
|
|
|
|
def __nonzero__(self):
|
|
# 'if Call()' must never be true to allow easier results from
|
|
# Capi.call_...()
|
|
return 0
|
|
|
|
def __str__(self):
|
|
return str(self._handle)
|
|
|
|
def __repr__(self):
|
|
# todo: add service, call_from, call_to
|
|
return ('Call(%(_handle)s, service=%(service)s, '
|
|
'from_nr=%(from_nr)s, to_nr%(to_nr)s)') % self.__dict__
|
|
|
|
|
|
###--- general --###
|
|
|
|
def disconnect(self):
|
|
"""
|
|
Disconnect connection.
|
|
|
|
This will cause an immediate disconnection. It should be
|
|
always the last command in every flow of a script.
|
|
|
|
Returns a tuple of two result values. The first is the
|
|
disconnect cause of the physical connection, the second the
|
|
disconnect cause of the logical connection. See CAPI spec for
|
|
the logical causes and ETS 300 102-01 for the physical causes.
|
|
"""
|
|
result = _capisuite.disconnect(self._handle)
|
|
return result
|
|
|
|
|
|
def reject(self, rejectCause):
|
|
"""
|
|
Reject an incoming call.
|
|
|
|
If you don't want to accept an incoming call for any reason
|
|
(e.g. if it has a service or comes from a number you don't
|
|
want to accept), use this command. There are several reasons
|
|
you can give when rejecting a call. Some important ones are:
|
|
|
|
rejectCause: cause to signal when rejecting call. This may be one of
|
|
1 = ignore call
|
|
2 = normal call clearing
|
|
3 = user busy
|
|
7 = incompatible destination
|
|
8 = destination out of order
|
|
0x34A9 = temporary failure
|
|
"""
|
|
_capisuite.reject(self._handle, rejectCause)
|
|
|
|
def log(self, message, level):
|
|
"""
|
|
Log a connection dependent message.
|
|
|
|
This function writes a message to the CapiSuite log. As all messages
|
|
written with it are prefixed with the current call reference, you
|
|
should use it for connection-dependant messages (e.g. information about
|
|
handling *this* call).
|
|
|
|
If you want to log messages of general nature not associated with a
|
|
certain call (e.g. problem in reading configuration files), please use
|
|
core.log instead.
|
|
|
|
message: the log message to be written
|
|
level: parameter for CapiSuite log_level used (0=vital .. 3=debug info)
|
|
"""
|
|
_capisuite.log(message, level, self._handle)
|
|
|
|
###--- DTMF support --###
|
|
|
|
def enable_DTMF(self):
|
|
"""
|
|
Enable recognition of DTMF tones.
|
|
"""
|
|
_capisuite.enable_DTMF(self._handle)
|
|
|
|
def disable_DTMF(self):
|
|
"""
|
|
Disable recognition of DTMF tones.
|
|
"""
|
|
_capisuite.disable_DTMF(self._handle)
|
|
|
|
|
|
def read_DTMF(self, timeout, min_digits=0, max_digits=0):
|
|
"""
|
|
Read the received DTMF tones or wait for a certain amount of
|
|
them.
|
|
|
|
This function allows to just read in the DTMF tones which were
|
|
already received. But it also supports to wait for a certain
|
|
amount of DTMF tones if you want the user to always input some
|
|
digits at a certain step in your script.
|
|
|
|
You can specify how much DTMF tones you want in several ways -
|
|
see the parameter description. To just see if something was
|
|
entered before, use capisuite.read_DTMF(0). If you want to get
|
|
at least 1 and mostly 4 digits and want to wait 5 seconds for
|
|
additional digits, you'll use capisuite.read_DTMF(5,1,4).
|
|
|
|
Valid DTMF characters are '0'...'9','A'...'D' and two special
|
|
fax tones: 'X' (CNG), 'Y' (CED)
|
|
|
|
timeout: timeout in seconds after which reading is terminated;
|
|
only applied after min_digits have been read! (-1 =
|
|
infinite)
|
|
min_digits: minimum number of digits which must be read in ANY
|
|
case, i.e. timout doesn't count here (default: 0)
|
|
max_digits: maximum number of digits to read; aborts
|
|
immediately enough digits are read) (default:
|
|
0=infinite, i.e. wait until timeout is reached)
|
|
|
|
Returns a string containing the characters read.
|
|
"""
|
|
# todo: descibe what A...D means and where '#' and '*' go
|
|
return _capisuite.read_DTMF(self._handle, timeout,
|
|
min_digits, max_digits)
|
|
|
|
|
|
###--- voice calls ---###
|
|
|
|
def connect_voice (self, delay=0):
|
|
"""
|
|
Accept an incoming call and connect with voice service.
|
|
|
|
This will accept an incoming call and choose voice as service,
|
|
so you can use the audio commands (like audio_receive() and
|
|
audio_send()) with this connection. After this command has
|
|
finished, the call is connected successfully.
|
|
|
|
It's also possible to accept a call with some delay. This is
|
|
useful for an answering machine if you want to fetch a call
|
|
with your phone before your computer answers it.
|
|
|
|
delay: delay in seconds _before_ connection will be established
|
|
(default: 0=immediate connect)
|
|
"""
|
|
_capisuite.connect_voice(self._handle, delay)
|
|
|
|
|
|
def audio_receive(self, filename, timeout, silence_timeout=0,
|
|
exit_DTMF=0):
|
|
"""
|
|
Receive an audio file in a speech mode connection.
|
|
|
|
This functions receives an audio file. It can recognize
|
|
silence in the signal and timeout after a given period of
|
|
silence, after a general timeout or after the reception of a
|
|
DTMF signal.
|
|
|
|
If the recording was finished because of silence_timeout, the
|
|
silence will be truncated away.
|
|
|
|
If DTMF abort is enabled, the command will also abort
|
|
immediately if DTMF was received before it is called. This
|
|
allows to abort subsequent audio receive and send commands
|
|
with one DTMF signal w/o the need to check for received DTMF
|
|
after each command.
|
|
|
|
The connction must be in audio mode (by connect_voice()),
|
|
otherwise an exception will be caused.
|
|
|
|
The created file will be saved in bit-reversed A-Law format, 8
|
|
kHz mono. Use sox to convert it to a normal wav file.
|
|
|
|
filename: where to save the received message.
|
|
timeout: receive length in seconds (-1 = infinite).
|
|
silence_timeout: abort after x seconds of silence (default: no timeout)
|
|
exit_DTMF: abort sending when a DTMF signal is received (default: 0)
|
|
|
|
Returns duration of receiving in seconds.
|
|
"""
|
|
return _capisuite.audio_receive(self._handle, filename, timeout,
|
|
silence_timeout, exit_DTMF)
|
|
|
|
|
|
def audio_send(self, filename, exit_DTMF=0):
|
|
"""
|
|
Send an audio file in a speech mode connection.
|
|
|
|
This function sends an audio file, which must be in
|
|
bit-inversed A-Law format. Thus files can be created with eg.
|
|
sox using the suffix ".la". It supports abortion if DTMF
|
|
signal is received.
|
|
|
|
If DTMF abort is enabled, the command will also abort
|
|
immediately if DTMF was received before it is called. That
|
|
allows you to abort subsequent audio receive and send commands
|
|
with one DTMF signal w/o the need to check for received DTMF
|
|
after each command.
|
|
|
|
The connction must be in audio mode (use connect_voice()),
|
|
otherwise an exception will be caused.
|
|
|
|
filename: file to send
|
|
exit_DTMF: abort sending when a DTMF signal is received (default: 0)
|
|
|
|
Returns duration of send in seconds.
|
|
"""
|
|
return _capisuite.audio_send(self._handle, filename, exit_DTMF)
|
|
|
|
|
|
def switch_to_faxG3(self, faxStationID, faxHeadline):
|
|
"""
|
|
Switch a connection from voice mode to fax mode.
|
|
|
|
This will switch from voice mode to fax group 3 after you have
|
|
connected, so you can use the fax commands afterwards.
|
|
|
|
Attention: Not all ISDN cards or CAPI driver support this
|
|
command.
|
|
|
|
faxStationID: the station ID to use (string)
|
|
faxHeadline: the fax headline to use (string)
|
|
|
|
Returns a FaxInfo instance.
|
|
"""
|
|
faxInfo = _capisuite.switch_to_faxG3(self._handle,
|
|
faxStationID, faxHeadline)
|
|
self.service = SERVICE_FAXG3
|
|
log('faxinfo: %s' % repr(faxInfo), 3)
|
|
if not faxInfo:
|
|
return FaxInfo()
|
|
return FaxInfo(*faxInfo)
|
|
|
|
|
|
###--- fax calls --###
|
|
|
|
def connect_faxG3(self, faxStationID, faxHeadline, delay=0):
|
|
"""
|
|
Accept an incoming call and connect with fax (analog, group 3) service.
|
|
|
|
This will accept an incoming call and choose fax group 3 as
|
|
service, so you can use the fax commands (like fax_receive())
|
|
with this connection. After this command has finished, the
|
|
call is connected successfully.
|
|
|
|
It's also possible to accept a call with some delay. This is
|
|
useful if eg. you want to have to fetch a call with your phone
|
|
before your computer answers it.
|
|
|
|
faxStationID: the station ID to use (string)
|
|
faxHeadline: the fax headline to use (string)
|
|
delay: delay in seconds _before_ connection will be established
|
|
(default: 0=immediate connect)
|
|
|
|
Returns a FaxInfo instance.
|
|
"""
|
|
faxInfo = _capisuite.connect_faxG3(self._handle, faxStationID,
|
|
faxHeadline, delay)
|
|
log('faxinfo: %s' % repr(faxInfo), 3)
|
|
if not faxInfo:
|
|
return FaxInfo()
|
|
return FaxInfo(*faxInfo)
|
|
|
|
|
|
def fax_send(self, faxfilename):
|
|
"""
|
|
Send a fax in a fax mode connection.
|
|
|
|
This command sends an analog fax (fax group 3). It starts the
|
|
send and waits for the end of the connection. So it should be
|
|
the last command before disconnect().
|
|
|
|
The connction must be in fax mode (use capi.call_faxG3() or
|
|
call.switch_to_faxG3()), otherwise an exception will be caused.
|
|
|
|
The file to be sent must be in the Structured Fax File (SFF)
|
|
format.
|
|
|
|
faxfilename: file to send
|
|
"""
|
|
faxInfo = _capisuite.fax_send(self._handle, faxfilename)
|
|
log('faxinfo: %s' % repr(faxInfo), 3)
|
|
if not faxInfo:
|
|
return FaxInfo()
|
|
return FaxInfo(*faxInfo)
|
|
|
|
|
|
def fax_receive(self, filename):
|
|
"""
|
|
Receive a fax in a fax mode connection.
|
|
|
|
This command receives an analog fax (fax group 3). It starts
|
|
the reception and waits for the end of the connection. So it
|
|
should be the last command before disconnect().
|
|
|
|
The connction must be in fax mode (use capi.call_faxG3() or
|
|
call.switch_to_faxG3()), otherwise an exception will be caused.
|
|
|
|
The created file will be saved in the Structured Fax File
|
|
(SFF) format.
|
|
|
|
filename: where to save the received fax.
|
|
"""
|
|
faxInfo = _capisuite.fax_receive(self._handle, filename)
|
|
log('faxinfo: %s' % repr(faxInfo), 3)
|
|
if not faxInfo:
|
|
return FaxInfo()
|
|
return FaxInfo(*faxInfo)
|
|
|
|
|
|
class FaxInfo:
|
|
def __init__(self, stationID='', rate=0, hiRes=0, format=0, numPages=0):
|
|
self.stationID = stationID
|
|
self.bitRate = rate
|
|
self.hiRes = hiRes
|
|
self.resolution = hiRes and "hiRes" or "loRes"
|
|
# cff: color fax; sff: normal black-and-white fax
|
|
self.format = format and 'cff' or 'sff'
|
|
self.color = format and 'color' or 'b&w'
|
|
self.numPages = numPages
|
|
|
|
def as_dict(self):
|
|
d = {}
|
|
for a in ('stationID', 'bitRate', 'resolution',
|
|
'hiRes', 'format', 'color', 'numPages'):
|
|
d[a] = getattr(self, a)
|
|
return d
|
|
|
|
# implemented in _capisuite:
|
|
#
|
|
#def error(...):
|
|
# pass
|
|
#def log(...):
|
|
# pass
|