@sipcentric/pbx-client

2.2.1 • Public • Published

Sipcentric PBX Client

A Node.js client for interacting with the Sipcentric PBX.

Contents

Key features

  • Implements all of the Sipcentric REST API endpoints to make interacting with the API simple.
  • Wraps JsSIP to allow you to easily make and receive calls through the Sipcentric PBX using WebRTC and WebSockets.
  • Works in Node.js and in the browser (making/receiving calls only works in the browser).

Useful resources

Getting started

npm install @sipcentric/pbx-client
const Sipcentric = require('@sipcentric/pbx-client');

const sipcentric = new Sipcentric({
  username: 'myusername',
  password: 'mypassword',
});

// ...

Examples

There are some example projects which use this library in the examples/ directory.

  • Softphone App - A simple softphone which allows you to make and receive calls from your browser.
  • Presence Viewer - Displays the current state of all extensions on your customer account in the terminal.

To try these examples, just follow the instructions in their respective READMEs.

Interacting with the REST API

The REST API is accessed through the authenticated instance of the Sipcentric class.

const Sipcentric = require('@sipcentric/pbx-client');

const sipcentric = new Sipcentric({
  username: 'myusername',
  password: 'mypassword',
});

// The API can now be accessed using the `sipcentric` variable

Promises and callbacks

All of the examples in this README use Promises and async/await, however this library also supports callbacks. To use callbacks, simply pass a callback as the final parameter and a promise won't be returned.

// Using promises with async/await
try {
  const customers = await sipcentric.customers.get();
  doStuffWith(customers);
} catch (err) {
  // Errors may have some of the following properties
  console.log('message: ', error.message); // Error message
  console.log('status: ', error.statusCode); // HTTP status code returned from the API
  console.log('body: ', error.responseBody); // The body of the API response
  console.log('response: ', error.response); // The full response object
}

// Using promises with .then()
sipcentric.customers.get()
  .then((customers) => {
    doStuffWith(customers);
  })
  .catch((err) => {
    // Errors may have some of the following properties
    console.log('message: ', error.message); // Error message
    console.log('status: ', error.statusCode); // HTTP status code returned from the API
    console.log('body: ', error.responseBody); // The body of the API response
    console.log('response: ', error.response); // The full response object
  });

// Using callbacks
sipcentric.customers.get((err, customers) => {
  if (err) {
    // Errors may have some of the following properties
    console.log('message: ', error.message); // Error message
    console.log('status: ', error.statusCode); // HTTP status code returned from the API
    console.log('body: ', error.responseBody); // The body of the API response
    console.log('response: ', error.response); // The full response object
    return;
  }

  doStuffWith(customers);
});

Create customer

To create a Sipcentric customer, simply pass the required data to the .create() method on sipcentric.customers, then call .save().

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customerData = {
    firstName: 'Fred',
    lastName: 'Bloggs',
    company: 'Some Great Company',
    address1: '123 Some Street',
    city: 'Birmingham',
    email: 'support@greatcompany.io',
    postcode: 'BA0 6ER',
    telephone: '01234567890',
  };

  const createdCustomer = await sipcentric.customers
    .create(customerData)
    .save();

  console.log(createdCustomer);
  // { type: 'customer', id: '1234', company: 'Some Great Company', postcode: 'BA0 9ER', ...}
})();

List customers

To fetch a list of customers that your user has access to, you can do as follows.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customerList = await sipcentric.customers.get();

  const customers = customerList.items;
  console.log(customers);
  // [{ type: 'customer', id: '1234', company: 'Some Great Company', ...}, {}, {}, ...]
})();

Here, .get() will return a promise which resolves to a CustomerList object. The list of customers returned from the API can be accessed through the .items property of the CustomerList.

Fetch a specific customer

If you already know the ID of the customer you'd like to fetch, you can specify that ID when calling .get().

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customer = await sipcentric.customers.get('5678');
  console.log(customer);
  // { type: 'customer', id: '5678', company: 'Another Great Company', postcode: 'BA0 9ER', ...}

  console.log(customer.email);
  // support@greatcompany.io
})();

In this case, .get()'s returned promise will resolve to an individual Customer object, rather than a CustomerList, meaning you can access the properties of the customer returned from the API directly on the Customer object.

List a customer's phone book

Once you've got a Customer object, you can use that to access specific endpoints relating to that customer. For example, if you wanted to fetch a customer's phone book you would do as follows.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customer = await sipcentric.customers.get('1234');
  const phonebook = await customer.phonebook.get();

  console.log(phonebook);
  // [{ type: 'phonebookentry', id: '359', name: 'Joe Bloggs' ...}, {}, {}, ...]
})();

Fetch a single phone book entry

You can fetch a single phone book entry in the same way you fetch a single customer; just pass an ID to the .get() function.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customer = await sipcentric.customers.get('1234');
  const phonebookEntry = await customer.phonebook.get('359');

  console.log(phonebookEntry);
  // { type: 'phonebookentry', id: '359', name: 'Joe Bloggs', phoneNumber: '01234567890', ...}
})();

Update a phone book entry

To update a phone book entry, just fetch that entry, make your desired changes, then call .save() on the entry. .save() will return a promise which resolves to the updated phone book entry.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });


  const customer = await sipcentric.customers.get('1234');
  const phonebookEntry = await customer.phonebookEntry.get('359');

  phonebookEntry.name = 'Joseph Bloggs';

  const savedEntry = await phonebookEntry.save();

  console.log(savedEntry);
  // { type: 'phonebookentry', id: '359', name: 'Joseph Bloggs', phoneNumber: '01234567890', ...}
})();

Create a phone book entry

To create a phone book entry, create an object with all of the desired properties, pass that object to the .create() method, then call .save().

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customer = await sipcentric.customers.get('1234');

  const phonebookEntry = {
    name: 'Sipcentric',
    phoneNumber: '03301200030',
    email: 'hello@sipcentric.com',
    speedDial: 19,
  };

  const createdEntry = await customer.phonebook
    .create(phonebookEntry)
    .save();

  console.log(createdEntry);
  // { type: 'phonebookentry', id: '360', name: 'Sipcentric', phoneNumber: '03301200030', ...}
})();

Delete a phone book entry

To delete a phone book entry, first fetch the entry, then call delete() on it. .delete() returns a promise which doesn't resolve to anything.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  const customer = await sipcentric.customers.get('1234');
  const phonebookEntry = await customer.phonebook.get('360');

  await phonebookEntry.delete();
})();

Interacting with other resources

The examples above show how to fetch customers and phone book entries. The same methods are available across all resources.

  • .get(id?: string) - List a resource or, if an ID is passed, fetch an individual representation.
  • .create(obj: {}) - Create a representation from an object. Must then call .save() on the returned representation to persist it to the API.
  • .save() - Persist a representation to the API.
  • .delete() - Delete a representation from the API.

You can find out a bit more about the available resources in the API Documentation. The resources that this client supports are listed below. We endeavour to keep this library up-to-date with the current state of the API, however if there is a resource listed in the API docs that isn't available here, please open an issue and it will be added as a matter of priority.

sipcentric
├──.customers
│   ├──.availablebundles
│   ├──.billing
│   │   ├──.invoices
│   │   ├──.estimate
│   │   └──.paymentmethods
│   ├──.calls
│   ├──.callbundles
│   ├──.creditstatus
│   ├──.endpoints
│   ├──.groups
│   ├──.ivrs
│   ├──.linkedusers
│   ├──.mailboxes
│   ├──.music
│   ├──.outgoingcallerids
│   ├──.phones
│   │   ├──.forwardingrules
│   │   └──.sip
│   │       └──.registrations
│   ├──.phonebook
│   ├──.phonenumbers
│   │   └──.routingrules
│   ├──.prompts
│   ├──.preferences
│   ├──.queues
│   │   ├──.entries
│   │   ├──.memberships
│   │   └──.status
│   ├──.recordings
│   ├──.smsmessages
│   ├──.sounds
│   ├──.timeintervals
│   └──.virtuals
├──.getUA()
└──.stream

Interacting with the Streaming API

The streaming API uses long-polling HTTP requests to keep a connection open for events to be pushed down.

There are a variety of events which are exposed through the stream. For more information on those events see the API Documentation.

Subscribe to stream events

const Sipcentric = require('@sipcentric/pbx-client');

const sipcentric = new Sipcentric({
  username: 'myusername',
  password: 'mypassword',
});

// We'll use the 'incomingcall' event, in this example
sipcentric.stream.subscribe('incomingcall', (call) => {
  console.log(call);
});

Interacting with the PBX using WebRTC

The getUA() method accepts a single optional parameter, which is a config object, and returns a (slightly modified) JsSIP User Agent.

interface Config {
  // Whether the User Agent should register (required for receiving calls)
  register?: boolean; // Default - false

  // The instanceId (uuid) to send with the registration
  instanceId?: string; // Defaults to a generated uuid

  // The customer to use.
  customerId?: string; // Defaults to first available customer

  // The extension to use.
  extensionId?: string; // Defaults to linkedUser's default extension

  // The SIP username of the extension to use
  username?: string; // Defaults to default extension's username

  // The SIP password of the extension to use
  password?: string; // Defaults to default extension's password

  // Refs to the <audio> elements to use for local and remote audio
  audio?: {
    local?: HTMLAudioElement,
    remote?: HTMLAudioElement,
  } // Defaults to undefined
}

const config: Config = {
  // ...
};

const ua = await sipcentric.getUA(config);

Differences between Sipcentric UA and JsSIP UA

The getUA() method returns a promise which resolves to an instance of a Sipcentric UA, which itself extends a JsSIP UA. It's worth noting that getUA() does not accept a final callback parameter, it only ever returns a promise.

There are a few minor differences between the Sipcentric UA and the JsSIP UA, which are outlined below. Other than these listed differences, the two are identical and you can refer to JsSIP's documentation for more information on interacting with the UA and with RTCSessions.

Differences

  • JsSIP's .call(target, options) method has been replaced with .dial(target, options). You can pass the same options to .dial() as you would .call(), but .dial() sets a few useful defaults for you.
  • Two new methods have been added.
    • .subscribeToUser(user) subscribes to a user's presence and fires a userStateChanged event when a user's state changes (see below for more information).
    • .clearSubscriptions() clears all subscriptions.
  • A userStateChanged event has been added, which is emitted when a user's presence state changes. Two parameters are also passed when this event is emitted. The first is the user who's state has changed, the second is the new state. See Monitor the presence of an extension for more information and an example.

Create a UA and connect

Creating a UA is simple. By default, the getUA() method will do a lot of the heavy lifting for you. It will fetch the first customer you have access to, fetch your linkeduser on that customer, fetch that linkeduser's default extension, then use that extension's SIP credentials to connect. This means that you don't need to pass any parameters to it to get up and running.

If you'd like to connect using a different extension, simply pass an options object to getUA() which includes an extensionId and a customerId. See Interacting with the PBX using WebRTC for more information on the options parameter.

In this example, we'll get an instance of a UA for a specific extension.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  // Create an instance of a user agent, connected to extension 12345
  const ua = await sipcentric.getUA({
    extensionId: '12345',
  });

  ua.on('connected', () => {
    // Do things that rely on a connection here
  });

  // Start and connect the user agent
  ua.start();
})();

Monitor the presence of an extension

After subscribing to an extension using .subscribeToUser(user), events will be fired every time that extension's state changes. The userStateChanged event also passes two parameters when it's emitted; the user who's state has changed (useful if you've subscribed to multiple users), and the user's new state.

  • AVAILABLE - The user isn't on a call
  • BUSY - The user is connected to a call
  • RINGING - The user's phone is currently ringing
const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  // Create an instance of a user agent
  const ua = await sipcentric.getUA({
    extensionId: '12345',
  });

  // Set up our event listener
  ua.on('userStateChanged', (extension, newState) => {
    console.log(extension); // Could be either 012345 or 567890
    console.log(newState); // AVAILABLE, BUSY, or RINGING
  });

  // Wait until the ua has connected before subscribing
  ua.on('connected', () => {
    // Subscribe to some extensions by passing their SIP username
    ua.subscribeToUser('012345');
    ua.subscribeToUser('567890');
  });

  // Start and connect the user agent
  ua.start();
})();

Register an extension

If you want to receive calls, you'll need to make sure that you're user agent is registered. This library handles re-registering for you, so all you need to do is pass { register: true } to getUA().

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  // Create an instance of a user agent, passing { register: true }
  const ua = await sipcentric.getUA({
    extensionId: '12345',
    // Make sure we register once connected
    register: true,
    // Bind our local and remote <audio> elements by passing refs
    audio: {
      local: localAudioRef,
      remote: remoteAudioRef,
    }
  });

  ua.on('registered', () => {
    // Do things that rely on a registration here
  });

  ua.on('registrationFailed', (data) => {
    // Log out any errors
    console.log('registration failed', data.cause);
  });

  // Start, connect, and register the user agent
  ua.start();
})();

Make a call

To make a call, you'll need access to browser APIs, so you'll likely need to use a bundler (Webpack, Rollup, etc) to bundle this library to run in a browser.

Once you've got the library running in a browser, you'll be able to call the .dial() method on the ua.

You can learn more about handling ongoing calls in JsSIP's RTCSession documentation.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  // Create an instance of a user agent, passing refs to your <audio> elements
  const ua = await sipcentric.getUA({
    extensionId: '12345',
    audio: {
      local: localAudioRef,
      remote: remoteAudioRef,
    },
  });

  // For the sake of simplicity, we'll dial a number as soon as we connect
  ua.on('connected', () => {
    // *52 is an echo test, which is useful for development
    const session = ua.dial('*52'); // Change this to the number you'd like to call

    // Do things with the session here (.mute(), .hold(), .terminate(), etc)

    // Let's put the call on hold for a few seconds
    session.hold();

    setTimeout(() => {
      // If it's still on hold
      if (session.isOnHold()) {
        // Take it off hold
        session.unhold();
      }
    }, 3000);
});

  // Start and connect the user agent
  ua.start();
})();

Receive a call

To receive incoming calls, you'll first need to make sure your ua is registered, then you'll need to listen for the newRTCSession event, which is emitted for both incoming and outgoing calls. Because it's emitted for all calls, you'll need to check that the originator of the call is 'remote' to ensure that it's an inbound call. Once you're happy, you can call call.session.answer() to answer the call.

You can learn more about handling ongoing calls in JsSIP's RTCSession documentation.

const Sipcentric = require('@sipcentric/pbx-client');

(async () => {
  const sipcentric = new Sipcentric({
    username: 'myusername',
    password: 'mypassword',
  });

  // Create an instance of a user agent, passing refs to your <audio> elements
  const ua = await sipcentric.getUA({
    extensionId: '12345',
    // Make sure you're registered if you want to receive incoming calls
    register: true,
    audio: {
      local: localAudioRef,
      remote: remoteAudioRef,
    },
  });

  ua.on('registered', () => {
    console.log('registered');
  });

  ua.on('registrationFailed', (data) => {
    // Log out any errors
    console.log('registration failed', data.cause);
  });

  // Fired on a new call, inbound or outbound
  ua.on('newRTCSession', (call) => {
    // Make sure it's an inbound call
    if (call.originator === 'remote') {
      // Let's answer the call straight away
      call.session.answer();
    }
  });

  // Start and connect the user agent
  ua.start();
})();

Package Sidebar

Install

npm i @sipcentric/pbx-client

Weekly Downloads

0

Version

2.2.1

License

MIT

Unpacked Size

82.6 kB

Total Files

75

Last publish

Collaborators

  • sipcentric