Paxton Net2, meet Slack.

December 2017 : As of recent versions of net2, the way SMS messages are sent has been changed, which breaks this hack, an update will follow. The advice below may still help you with other hard-coded SMS solutions.

This is my (somwhat hacky) attempt to make Paxton net2 talk to Slack, however the basic principles will work with a lot of hard-coded SMS solutions

I like Slack, I use it for work and more personal use - even the team behind this blog engine Ghost have their own slack team for their community, sharing information and providing support in a unique - but very effective way.

I also use the Paxton net2 system to control doors, and receive notifications by eMail when various things of interest happen. It would be so much easier to have these in a Slack channel, and would enable instant push notifications to iPhone.

Net2 API

Paxton net2 also has an 'API', but not a nice HTTP-based thing, it's a collection of cryptic functions in a language I don't understand (.net), written for an OS I avoid using (Windows).

There's a great collection of scripts by net2-solutions that use Python, but I don't really know - or have any desire to learn - Windows well enough to make daemons to connect to net2 and expose it in a sensible way (if someone does this, please, please share the source)

Built-in SMS

Paxton supports SMS out of the box, however they only support one provider - textanywhere.net - and choosing who to notify is cumbersome (if you've spent much time managing "Triggers and Actions" in Net2, you'll know what I mean)

However, short messages are exactly what I want in Slack, and it turns out there's a really easy way to achieve this in net2, as long as you don't currently use SMS.

Remapping Hosts

Most operating systems provide a 'hosts file', a simple way of hardcoding a hostname to an IP address, negating the need for a DNS lookup - this allows you to hijack traffic intended for somewhere else.

The net2 software uses a simple HTTP API to connect to the SMS provider, and makes a call to http://ws.textanywhere.net/ta_SMS.aspx to send messages.

By adding the following line to C:\Windows\System32\Drivers\etc\hosts, we can redirect this traffic to a server of our choice;

10.0.7.30 ws.textanywhere.net

In my case 10.0.7.30 is a Linux host on the local network (actually a Raspberry Pi, I use these for practically everything!) running a simple web server.

See what's being sent

If you're using this with net2, there is no need to do this, just see the example below which details the request format. You can also consult the API documentation of the SMS provider.

You could use Wireshark or similar, but sometimes the simplest means are the best. I configured the web server to send all requests to a simple PHP script as follows;

<?php
$fp = fopen('php://input', 'r');
$x = stream_get_contents($fp);
file_put_contents('/tmp/request.txt', $x, FILE_APPEND);

Unfortunately this particular provider uses an XML request in the body, a simpler API would use HTTP POST requests, so you could just use print_r($_REQUEST,true) instead of the stream_get_contents() function

Send a test message and you'll get a file in your /tmp directory showing the variables that you need to process later.

Configure net2

This is simple, just configure it as if it was an SMS, and put anything at all in the To line (we'll ignore this), rather than formatting a sensible message I've used the same format for every event, this makes it easier to parse later.

The script

On our server we need a simple ta_SMS.asmx script to receive the requests from net2, in my case I've used php so needed a simple .htaccess file containing the following;

<Files ta_SMS.asmx>
ForceType application/x-httpd-php
</Files>

This forces Apache to treat ts_SMS.asmx as if it was a php file (even though it has a .asmx extension)

The script itself can be quite simple (ours also takes advantage of the data and logs it to MySQL)

<?php
// get the input 
$fp = fopen('php://input', 'r');
$x = stream_get_contents($fp);
// this is a horrible hack to remove namespaces
$x = str_replace('soap:','',$x);
$xml = simplexml_load_string($x);
// and another horrible hack gives us a simpler object 
$objReq = json_decode(json_encode($xml->Body->SendSMS));

// this is the URL slack have provided for your webhook
$strSlackUrl = 'https://hooks.slack.com/services/XXXXXXXXX/XXXXX';

// The objReq request object contains everything that the
// original provider needed to send an SMS, including;
//   Client_ID,  Client_Pass,  Originator,  OType
//   Destination,  SMS_Type and Body
// we ignore almost all of these.

// we use both of these in the IF statement, purely
// to ensure this is a valid request, you could put
// some authentication here if you like
if ($objReq->Client_ID && $objReq->Client_Pass) {
    $strMsg = (string)$objReq->Body;
    $arrLines = split("\n",$strMsg);

    // arrLines now contains the variables we specified above
    // in the net2 config (this is why there's one variable
    // per line, rather than an actual message) 
    $strUser = $arrItems[2];
    $strDate = $arrItems[3];
    $strTime = $arrItems[4];
    $strDoor = $arrItems[5];
    $strAction = $arrItems[6];
    $strToken = $arrItems[7];

    // Here is where you build the message, you can adjust it
    // based on the type of request (this is why we split it 
    // into different lines!)
    
    // for example, set a colour
    $slackColour = '#cccccc';
    if (strpos($strAction,'permitted')) $slackColour = 'good';
    if (strpos($strAction,'denied')) $slackColour = 'danger';

    // the message itself
    $strMessage = "$strDoor - $strAction";

    // and build a message for slack, with nicely formatted
    // fields that will be displayed
    $arrSlackVars[] = Array('title' => 'user', 'value' => $strUser, 'short' => true);
    $arrSlackVars[] = Array('title' => 'door', 'value' => $strDoor, 'short' => true);
    $arrSlackVars[] = Array('title' => 'token', 'value' => $strToken, 'short' => true);
    // where to send the message
    $arrSlack = Array('channel' => '#net2', 'username' => 'net2')
    // the attachments (fields and colour)
    $arrSlack['attachments'] = Array(Array('fallback' => $strMessage,
                                           'pretext' => $strMessage,
                                           'color' => $slackColour,
                                           'fields' => $arrSlackVars));
    $jsonData = json_encode($arrSlack);
    $arrHeaders =  Array('Content-Type: application/json', 
                         'Content-Length: '.strlen($jsonData));

    $ch = curl_init($strSlackUrl);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
    curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $arrHeaders);
    curl_exec($ch);
}
echo "OK";

Try it!

As doors open, you'll see it in slack.

Extending this

Slack's bots are incredibly powerful, you can write one to ask who's in the building, or if you've got a large campus, where someone was last seen.

Also, you can create rules so that if alarm events are received (e.g. door forced, alarm activation etc) they go into your company's #general room so someone can investigate.

net2 API

I really want to do more with the dedicated net2 API, if I could find a way to hook into that with a script in Python or Node that just passed all the events across into 0MQ or redis or something that'd be perfect, but I expect that there won't be much more development on net2 now that the new net10 is out.