Calling a door phone from a web browser
Tags: javascript, routr, sip, webrtc
Published: 2024-02-23 18:27:26

Calling a door phone from a web browser

In this tutorial, I will take you through the steps of building a VoIP network that will allow you to make calls from a web browser out to a door phone. This setup would be helpful for access control for commercial buildings, monitoring and security, calls for assistance in hospitals, and more.

We will use SIP.js as the client, Routr as the signaling server, and RTPEngine to proxy RTP traffic.

{% github fonoster/routr no-readme %}

## Requirements

Before you start this tutorial, you will need the following:

* Docker Engine installed on your computer or the cloud
* NodeJS 18+ (Use nvm if possible)
* Routr command-line tool (Install with `npm install -g @routr/ctl`)
* A door phone such as Fanvil i20S

## Running Routr and RTPEngine with Docker Compose

This tutorial will use Routr for signaling and RTPEngine to proxy RTP traffic. 

The simplest way to run both services is using Docker Compose. 

To run the services with Docker Compose, first, create a folder named voipnet and, in it, a file named compose.yaml with the following content:

Filename: _voipnet/compose.yml_

```yaml
version: "3"

services:
  routr:
    image: fonoster/routr-one:latest
    environment:
      EXTERNAL_ADDRS: ${DOCKER_HOST_ADDRESS}
      RTPENGINE_HOST: rtpengine
    ports:
      - 51908:51908
      - 5062:5062
      - 5060:5060
    volumes:
      # Ensures the data survives container restarts
      - shared:/var/lib/postgresql/data

  # RTPEngine requires network mode "host" to work properly. However, this option doesn't work on 
  # Windows and MacOs. For development, we are opening a few ports to the host machine. 
  # For production, you must use the network_mode: host which works on Linux.
  rtpengine:
    image: fonoster/rtpengine:latest
    # Uncomment the following line for production
    # network_mode: host
    environment:
      # Set DOCKER_HOST_ADDRESS to an IP address that is reachable to the SIP clients
      PUBLIC_IP: ${DOCKER_HOST_ADDRESS}
      PORT_MIN: 10000
      PORT_MAX: 10100
      LOG_LEVEL: 6
    ports:
      - 22222:22222/udp
      - 10000-10100:10000-10100/udp

volumes:
  shared:
```

Notice how we included the environment variables EXTERNAL_ADDRS and PUBLIC_IP in the previous file, whose value must be set to an IP that all parties can reach to avoid wrong signaling and audio issues.

Also noteworthy is that the port 51908 was opened for administration, 5060 for TCP signaling, and 5062 for signaling with SIP.js

Next, save the file and run the following command:

```bash
# NOTE: Be sure to update the IP address
DOCKER_HOST_ADDRESS=192.168.1.7 docker compose up
```

The previous command will pull Routr and RTPEngine from Docker Hub and start the containers. You could also add the `-d` option to run the service in the background.

## Configuring a Domain and a set of Agents

You will use Routr's command-line tool to issue commands to the server and build the VoIP network.

The network will have a Domain, `sip.local`, an Agent named "Door Phone," and an Agent named "Admin."

To build the VoIP network, first create a new Domain with:

```bash
rctl domains create --insecure
```

Notice the `--insecure` flag, which is required since we don't have a TLS certificate.

The output of your command will look similar to the output below:

```bash
Press ^C at any time to quit.
 ›   Warning: Egress rules unavailable due to 0 configured numbers.
? Friendly Name Local Domain
? SIP URI sip.local
? IP Access Control List None
? Ready? Yes
Creating Domain Local Domain... 3b20410a-3c80-4f66-b7b3-58f65ff65352
```

Next, create two sets of credentials—one for the administrator and one for the door phone.

To create a set of credentials, issue the following command:

```bash
rctl credentials create --insecure
```

You must do this twice, one for the Door Phone and one for Admin. Please set the Door Phone username to `doorphone1` and the Admin to `admin`

Your output will be similar to this:

```bash
This utility will help you create a new set of Credentials.
Press ^C at any time to quit.
? Friendly Name Door Phone #1 - Credentials
? Username doorphone1
? Password [hidden]
? Ready? Yes
Creating Credentials Door Phone - Credentials... 5fbc7367-a59d-4555-9fc4-a15ff29c24c8
```

Finally, create Agents to represent the Door Phone and the Admin using the following command:

```bash
rctl agents create --insecure
```

Follow the prompt and ensure that the username matches that of the credentials.

Your output will look similar to this:

```bash
This utility will help you create a new Agent.
Press ^C at any time to quit.
? Friendly Name Door Phone #1
? Select a Domain sip.local
? Username doorphone1
? Credentials Name Door Phone #1 - Credentials
? Max Contacts 1
? Privacy None
? Enabled? Yes
? Ready? Yes
Creating Agent Door Phone #1... 662a379d-66f1-4e6e-9df5-5126f1dcb930
```

Be sure to repeat the process for the Admin.

You might use the `get` subcommand for any previously created resources to verify your settings. For example, to get a list of Agents, you might run this:

```bash
rctl agents get --insecure
```

## Configuring the door phone

The configuration on your door phone will depend on the make and model. However, generally speaking, it should have the following parameters:

- Username
- Password
- Domain
- Proxy/Server

For a Fanvil iS20, the configuration will be like this:

![Video feed from a Fanvil unit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g82xwnd99x5rwghrz42w.png)

## Building a barebones tool for calling

To create the SIP Agent, we will use the SimpleAgent implementation SIP.js, which is perfect for testing.

For a bare-bone SIP Agent, copy the following code in a file named index.html:

Filename _voipnet/index.html_

```html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>
  <meta charset="utf-8" />
  <title>Video Feed</title>
  <style>
    h1 { font-family: 'Courier New', Courier, monospace; }
  </style>
</head>

<body>
  <h1>Door Phone Video Feed</h1>
  <div style="border: 1px solid black;  width: 300px; height: 300px;">
    <video id="remoteVideo" autoplay muted style="background-color: black; height: 100%"></video>
  </div>

  <br />

  <button id="callButton">Call Door Phone</button>
  <button id="hangupButton">Hangup</button>

  <audio style="display: none" id="remoteAudio" controls>
    <p>Your browser doesn't support HTML5 audio.</p>
  </audio>
  <script type="module">
    import { Web } from "https://unpkg.com/sip.js@0.21.2/lib/index.js";

    document.addEventListener('DOMContentLoaded', () => {
      const remoteVideo = document.getElementById('remoteVideo');
      const callButton = document.getElementById('callButton');
      const hangupButton = document.getElementById('hangupButton');
      const remoteAudio = document.getElementById('remoteAudio');

      const config = {
        server: "ws://localhost:5062",
        aor:  "sip:admin@sip.local",
        doorPhoneAor: "sip:doorphone1@sip.local"
      }

      const options = {
        aor: config.aor,
        media: {
          constraints: { audio: true, video: true },
          remote: {
            audio: remoteAudio,
            video: remoteVideo
          }
        },
        userAgentOptions: {
          authorizationUsername: "admin",
          authorizationPassword: "1234",
        }
      };

      const simpleUser = new Web.SimpleUser(config.server, options);

      callButton.addEventListener('click', async() => {
        await simpleUser.connect();
        simpleUser.call(config.doorPhoneAor);
      });

      hangupButton.addEventListener('click', () => {
        simpleUser.hangup();
      });
    });
  </script>
</body>

</html>
```

Then, lunch the SIP Agent with:

```bash
npx http-server .
```

# Calling the door phone from the web browser

Now that created a SIP agent using SIP.js, we only need to register the door phone to Routr and press the "Call Door Phone" button in the web browser.

If you have issues calling the door phone, please inspect the browser's console for errors.

The SIP agent should connect to the door phone, and you should see a video feed.

Example of the output:

![Video feed from a Fanvil unit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74qjqlh0agkjmimfjckn.png)

## What's next?

Please comment if you find this tutorial helpful and check out the following relevant tutorials:

- [Building scalable IVRs for businesses with Routr and Asterisk](https://dev.to/fonoster/building-scalable-ivrs-for-businesses-with-routr-and-asterisk-cjd)
- [Browser-to-Browser calling with SIP.js and Routr](https://dev.to/fonoster/browser-to-browser-calling-with-sipjs-and-routr-3l07)
- [Simplify SIP.js security with short-lived tokens](https://dev.to/psanders/simplify-sipjs-security-with-short-lived-tokens-mb8)