@darkobits/doorman

0.2.0 • Public • Published

Modern apartment communities equipped with access control systems typically work like this:

  1. The resident gives their phone number (usually a cell) to their apartment community.
  2. When a guest arrives, they dial a number associated with the resident in the directory.
  3. The access control system calls the resident.
  4. The resident enters a digit or sequence of digits to open the door/gate for the guest.

This is annoying because of its synchronicity; it requires the resident to answer their phone, call quality is often very poor, and if anything goes wrong both parties wind up confused and frustrated. Additionally, this process does not scale well; a resident hosting a large event, for example, would have to remain tethered to their phone, anxious that one missed call will delay and frustrate one of their guests.

What if there was a better way?

  1. The resident gives their phone number (in this case, a Twilio number that points to a server running Doorman) to their apartment community.
  2. When a guest arrives, they dial a number associated with the resident in the directory.
  3. The access control system calls Doorman which, depending on configuration, will:
    • Immediately dial the digit or sequence of digits to open the door/gate for the guest
    • Send the resident an SMS letting them know someone has arrived.

This process is completely asynchronous; the resident need not answer their phone and guests are never needlessly delayed or confused.

Setup

Doorman is an HTTP server. To use it, you will need to install it someplace where HTTP servers like to live, such as Heroku, Now, et. al.

Next, you will need to set up a Twilio account. Create a TwiML App and configure the Voice URL to point to the server running Doorman.

Doorman is configured using a key/value store where each key is a phone number and each value is a JSON blob describing how Doorman should behave. You can use any key/value store you like, including an in-memory object, but Redis is ideal, and Heroku offers free nodes.

Installation

Doorman is available on NPM:

$ npm i @darkobits/doorman

Configuration

Doorman has the following configurable options:

Option Description
assetPath Path to a folder containing any static assets (such as audio files) you wish to serve.
port Which port to listen on. This is typically configured automatically by providers like Heroku. Default: 8080
primaryPhoneNumber If Doorman can't find a matching key in its data store for an incoming call, it will forward the call to this number.
twilioAccountSid Twilio Account SID.
twilioApplicationSid Twilio Application SID.
callDataFn This function will be invoked by Doorman and will be passed the current incoming caller ID and a callback. The callback has the signature (err, data) and should be invoked and passed the call flow data matching the provided caller ID.
logLevel How much logging information to display. Uses npmlog. (Default: info)
allowInsecure Whether to allow connections over HTTP (default: false).

Examples

Here is an example using Redis as a data store:

import { resolve } from 'path';
import redis from 'redis';
import doorman from '@darkobits/doorman';

const {
  PORT
  PRIMARY_PHONE_NUMBER,
  REDIS_URL,
  TWILIO_ACCOUNT_SID,
  TWILIO_APPLICATION_SID
} = process.env;


const client = new redis.createClient({
  url: REDIS_URL
});


doorman({
  assetPath: resolve(__dirname, 'assets'),
  port: PORT,
  primaryPhoneNumber: PRIMARY_PHONE_NUMBER,
  twilioAccountSid: TWILIO_ACCOUNT_SID,
  twilioApplicationSid: TWILIO_APPLICATION_SID,
  callDataFn: client.get.bind(client)
})
.startServer();

And here is an example using a basic in-memory data store:

import { resolve } from 'path';
import doorman from '@darkobits/doorman';

const {
  PORT
  PRIMARY_PHONE_NUMBER,
  REDIS_URL,
  TWILIO_ACCOUNT_SID,
  TWILIO_APPLICATION_SID
} = process.env;

const data = {
  // ...
};


doorman({
  assetPath: resolve(__dirname, 'assets'),
  port: PORT,
  primaryPhoneNumber: PRIMARY_PHONE_NUMBER,
  twilioAccountSid: TWILIO_ACCOUNT_SID,
  twilioApplicationSid: TWILIO_APPLICATION_SID,
  callDataFn: (callerId, cb) => cb(undefined, data[callerId])
})
.startServer();

Scripting Calls

Doorman is programmed using JSON. Each key in Doorman's data-store represents an inbound caller ID, and each value represents how Doorman should handle calls from that number. Doorman supports numerous directives which can be composed to construct a call flow.

The basic structure of a call flow is:

[
  ["directiveName", { /* Directive options. */ }],
  ["directiveName", { /* Directive options. */ }],
  ["directiveName", { /* Directive options. */ }]
]

Directives

forwardCall

Forwards the call to the provided number.

Option Type Description
value String Number to forward to.

Example:

[
  ["forwardCall", {
    "value": "+14155551212"
  }]
]

sendSms

Sends a text message.

Option Type Description
value String Body of the message.
to String Number to send the message to.

Example:

[
  ["sendSms", {
    "to": "+14155551212",
    "value": "Hello, world!"
  }]
]

say

Uses Twilio's text-to-speech feature to speak the provided message. See the Twilio <Say> documentation for allowed values.

Option Type Description
value String Message to speak.
[voice='woman'] String Voice to use.
[language='en-GB'] String Language to use.

Example:

[
  ["say", {
    "value": "Greetings!"
  }]
]

sendDigits

Sends a sequence of DTMF tones for the provided digit or digits.

Option Type Description
value String Digit or digit sequence.

Example:

[
  ["sendDigits", {
    "value": "1234"
  }]
]

gatherDigits

Pauses the call and waits for the caller to enter a sequence of digits. The call will then proceed down the branch matching the sequence entered. A default branch must be provided to handle cases where the caller does not enter a matching sequence.

Option Type Description
branches Object Object mapping possible responses to nested Doorman JSON blobs.

Example:

[
  ["gatherDigits", {
    "123": [
      ["directiveName", { /* Directive options. */ }]
    ],
    "456": [
      ["directiveName", { /* Directive options. */ }]
    ],
    "default": [
      ["directiveName", { /* Directive options. */ }]
    ]
  }]
]

play

Instructs Twilio to play the audio file at the provided URL. To ensure Doorman serves static assets correctly, configure the assetPath option to point to the folder containing your audio files.

Option Type Description
value String Path to the audio file (relative to Doorman's web root) to play.

Example:

[
  ["play", {
    "value": "assets/foo.mp3"
  }]
]

hangUp

Ends the call.

Note: This directive is typically not needed, as Doorman will end the call if it reaches the end of the call flow.

Example:

[
  ["hangUp"]
]

Examples

In the following examples, let's assume we are working with an access control system that has the phone number +14155551111, a resident who has a cell number +14155552222, and that residents must enter the digit 9 to grant access to guests. Note that if using a data store such as Redis, we would store the access control system's caller ID as a key and the call flow as a value. These examples use a plain object for clarity.

In this first example, we will program Doorman to immediately grant access to guests, then send an SMS to the resident.

{
  "+14155551111": [
    ["sendDigits", {
      "value": "9"
    }],
    ["sendSms", {
      "to": "+14155552222",
      "value": "A guest has arrived!"
    }]
  ]
}

Next, let's prompt the guest for a simple passcode (123) and forward the call to the resident if an incorrect passcode is entered:

{
  "+14155551111": [
    ["say", {
      "value": "Please enter your passcode."
    }],
    ["gatherDigits", {
      "123": [
        ["say", {
          "value": "Access granted!"
        }],
        ["sendDigits", {
          "value": "9"
        }],
        ["sendSms", {
          "to": "+14155552222",
          "value": "A guest has arrived!"
        }]
      ],
      "default": [
        ["forwardCall", {
          "value": "+14155552222"
        }]
      ]
    }]
  ]
}

 


Readme

Keywords

none

Package Sidebar

Install

npm i @darkobits/doorman

Weekly Downloads

2

Version

0.2.0

License

WTFPL

Unpacked Size

53.4 kB

Total Files

14

Last publish

Collaborators

  • darkobits