Toggle navigation

API (beta)

Introduction

This guide is under construction as we still work on public API finalization.

Architecture

Odoo and Asterisk are not connected directly. A special middleware called the OdooPBX Agent is used.

The Agents usually run on the same server where Asterisk is and does the following:

  • Connects to Asterisk Manager Interface (AMI)
  • Connects to Odoo /longpolling/poll controller to accept commands from Odoo (or uses a built-in HTTP server when Odoo bus is not accessible e.g. on odoo.sh).
  • Forwards Asterisk events to Odoo as defined in PBX -> Settinfs -> Event.

Let’s review all thease in details.

Asterisk Events

Usually when you build your own module you are interested in some events.

You can find the full list of Asterisk events on the Asterisk Events page.

Here is an example of CDR event:

<record id="cdr" model="asterisk_common.event">
    <field name="name">Cdr</field>
    <field name="source">AMI</field>
    <field name="model">asterisk_calls.call</field>
    <field name="method">create_cdr</field>
    <field name="delay">2</field>
</record>

When Asterisk emmits the CDR event it is received by the Agent and sent via JSON-RPC to Odoo model asterisk_calls.call and method create_cdr:

@api.model
def create_cdr(self, event):
    data = {
        'accountcode': event.get('AccountCode'),
        'src': event.get('Source'),
        'dst': event.get('Destination'),
        'dcontext': event.get('DestinationContext'),
        'clid': event.get('CallerID'),
        'channel': event.get('Channel'),
        'dstchannel': event.get('DestinationChannel'),
        'lastapp': event.get('LastApplication'),
        'lastdata': event.get('LastData'),
        'started': event.get('StartTime') or False,
        'answered': event.get('AnswerTime') or False,
        'ended': event.get('EndTime') or False,
        'duration': event.get('Duration'),
        'billsec': event.get('BillableSeconds'),
        'disposition': event.get('Disposition'),
        'amaflags': event.get('AMAFlags'),
        'uniqueid': event.get('UniqueID') or event.get('Uniqueid'),
        'linkedid': event.get('linkedid'),
        'userfield': event.get('UserField'),
        'system_name': event.get('SystemName'),
    }
    ...

You can process CDR data according to your requirements.

Asterisk Actions

There is a special method to send AMI actions to Asterisk.

See the full list of Asterisk Actions.

agent.action(…)

All parameters are optional and can be compined in any order:

  • status_notify_uid - res.user ID who will get status of running the command (Success / Error)
  • notify - sometimes it is not required to get a result of command, so instead of waiting for it return immediately.
  • callback - Odoo’s method that will receive Asterisk action result.
  • passpack - a tupple containing app & model and method, e.g. ('asterisk_common.user', 'call_originate_response').
  • delay - if set command will be delayed on the Agent before sent to Asterisk.

Examples

Ping

Here is an example of simple Ping action.

def ping_asterisk(self):
    self.ensure_one()
    try:
        self.action({'Action': 'Ping'}, status_notify_uid=self.env.uid)
    except AgentError as e:
        raise ValidationError('Agent error: {}'.format(e))

Call Originate

@api.model
def call_originate_request(self, system_name='asterisk', exten=None,
                           context=None, number=None, channel=None,
                           channel_id=None, other_channel_id=None,
                           variables=[], callerid='"" <>', timeout=None):
    # Generate channel ID to track it.
    if not channel_id:
        channel_id = uuid.uuid4().hex
    if not other_channel_id:
        other_channel_id = uuid.uuid4().hex
    if not timeout:
        timeout = int(self.env[
                'asterisk_common.settings'].get_param(
                'originate_timeout'))
    self.env.user.asterisk_user.agent.action({
        'Action': 'Originate',
        'Context': context,
        'Priority': '1',
        'Timeout': 1000 * timeout,
        'Channel': channel,
        'Exten': number,
        'Async': 'true',
        'EarlyMedia': 'true',
        'CallerID': callerid,
        'ChannelId': channel_id,
        'OtherChannelId': uuid.uuid4().hex,
        'Variable': variables,
        },
        passback={'uid': self.env.user.id},
        callback=('asterisk_common.user', 'call_originate_response'),
    )

@api.model
def call_originate_response(self, data):
    result = data.get('result') and data['result'][0]
    uid = data.get('passback', {}).get('uid', 0)
    if result:
        if result['Response'] == 'Error' and uid:
            message = result['Message']
            self.env.user.asterisk_notify(message, uid=uid, warning=True)
            logger.warning('ORIGINATE ERROR: %s', message)
    elif data.get('error') and uid:
        message = data['error'].get('message') or str(data['error'])
        self.env.user.asterisk_notify(message, uid=uid, warning=True)
        logger.warning('ORIGINATE ERROR: %s', message)
    return True

Configuration management

To be described.