Simple Alarm call center

Simple Alarm call center script. This script can by used as after script for Asterisk AlarmReceiver cmd.

This script read saved events from asterisk alarm receiver. If the event is important (Alarm event) script create asterisk call file which make a call and play sound message to selected numbers.

You can also receive formated events to email.

Notes:

For better results in communication you can try increase output gain on your ATA (default setting is -3)

FXS Port Output Gain: +1

You must set all other regional settings to match your alarm !!! (FXS Port Impedance, Ring Frequency, Ring Voltage, ...)

Other info for Asterisk AlarmReceiver cmd can be found here: http://www.voip-info.org/wiki/index.php?page=Asterisk+cmd+AlarmReceiver

Written by
Uroš Indihar
Alphito d.o.o.

Changelog
        0.1 Initial release
        0.2 1.12.1008
                Added mail support

License GNU GPL3.

Warranty: None. Use at your own risk !

Asterisk settings

Example: alarmreceiver.conf

[general]
timestampformat = %a %b %d, %Y @ %H:%M:%S %Z
eventcmd = /usr/local/bin/after_alarm_event.php
eventspooldir = /var/spool/asterisk/alarm_events
logindividualevents = yes
fdtimeout = 2000
sdtimeout = 200
loudness = 8192
------------------------------------------------------------------------------------------------

Example: sip.conf (pstn to sip part)

[31]
type=friend
context=phones
host=dynamic
secret=*******
callerid="Ademco Alarm" <31>
dtmfmode=inband
disallow=all
allow=ulaw

------------------------------------------------------------------------------------------------
Example: extension.conf

[internal]
;alarm receiver
exten => 560,1,Verbose(1|Extension 560 - Alarm Receiver)
exten => 560,n,Ringing()
exten => 560,n,Wait(2)
exten => 560,n,AlarmReceiver()
exten => 560,n,Hangup()

[alarmreport]
exten => start,1,Answer()
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(ha/alarm)
exten => start,n,Wait(1)
exten => start,n,Playback(vm-goodbye)
exten => start,n,Hangup()

Script after_alarm_event.php

Download source here:

#!/usr/bin/php -q
<?php
class ademcoEventParser {
        var $eventMap=array(
                110 => "Fire Alarm",                          
                121 => "Duress",
                122 => "Alarm, 24-hour Silent",
                123 => "Alarm, 24-hour Audible",              
                131 => "Alarm, Perimeter",                    
                132 => "Alarm, Interior",                      
                134 => "Alarm, Entry/Exit",
                135 => "Alarm, Day/Night",
                143 => "Alarm, Expansion Module",
                146 => "Silent Burglary",
                150 => "Alarm, 24-Hour Auxiliary",            
                301 => "AC Power",
                302 => "Low System Battery/Battery Test Fail",
                305 => "System Reset",
                333 => "Trouble or Tamper Expansion Module",
                351 => "Telco Line Fault",
                353 => "Long Range Radio Trouble",
                373 => "Fire Loop Trouble",
                374 => "Exit Error Alarm",
                380 => "Global Trouble, Trouble Day/Night",
                381 => "RF Supervision Trouble",
                383 => "RF Sensor Tamper",
                384 => "RF Sensor Low Battery",
                401 => "Disarmed, Armed AWAY (MAX), Armed AWAY",
                406 => "Cancel by User",
                407 => "Remote Arm/Disarm (Downloading)",
                408 => "Quick Arm AWAY/MAX",
                441 => "Disarmed/Armed STAY/INSTANT , Quick Arm STAY/INSTANT",
                570 => "Bypass",
                602 => "Periodic Test",
                606 => "AAV to follow",
                607 => "System Test",
                623 => "Event Log 80% Full",
                629 => "1-1/3 Day No Event",
        );
        var $eventsDir="/var/spool/asterisk/alarm_events/";
        var $eventPrefix="event-";
        var $lastEventSave="last_received_event";
        var $actionChannels=array( //ademco_id => array(channel1,channel2,...);
                "NNNN(ADEMCO_ID 1)"=>array(
                                "SIP/NNNNNN (Asterisk channel)",
                                "SIP/NNNNNN (Asterisk channel)",
                             ),
                "NNNN(ADEMCO_ID 2)"=>array(
                                "SIP/NNNNNN (Asterisk channel)",
                             ),
        );
        var $callFileDir="/var/spool/asterisk/outgoing/";

        var $actionChannelsMail=array( //ademco_id => array(Email1=>Name1,Email2=>Name2,...);
                "NNNN(ADEMCO_ID 1)"=>array(
                                "example@example.com"=>"My Name",
                                "example1@example.com"=>"My Name1",
                             ),
                "NNNN(ADEMCO_ID 2)"=>array(
                                "example@example.com"=>"My Name",
                             ),
        );
        var $emailFromName="Ademco Alarm Report";
        var $emailFrom="cron@alphito.si";

        var $callerId="Alarm Report <NNNNNNNNNNNN>";

        function setEventsDir($dir) {
                $this->eventsDir=$dir;
        }

        function getEventFiles() {
                $eventFiles=array();
                $eventPrefixLen=strlen($this->eventPrefix);
                $dh = opendir($this->eventsDir);
                while (($file = readdir($dh)) !== false) {
                        if (substr($file,0,$eventPrefixLen) == $this->eventPrefix) {
                                $eventFiles[$file]=filemtime($this->eventsDir.$file);
                        }
                }
                asort($eventFiles);
                closedir($dh);
                $this->eventFiles=$eventFiles;
        }

        function loop() {
                foreach ($this->eventFiles as $file => $mtime) {
                        $data=$this->parse($file);
                        foreach ($data["events"] as $event) {
                                //echo $event;
                                $e=$this->parseEvent($event);
                                if ($this->getSavedEvent() == $event) {
                                        //event is duplicated to last event
                                        $this->log($data["metadata"],$event,"Duplicated event",$e);
                                } else {
                                        $this->saveEvent($event);
                                        $this->log($data["metadata"],$event,"New event",$e);
                                        $this->action($e,$data["metadata"],$event);
                                }
                                        
                        }
                        //print_r($data);
                        //remove file
                        unlink($this->eventsDir.$file);
                }
        }

        function action($e,$metadata,$event) {
                if(intval($e["e"]) < 200) {
                        //do action on alarm events
                        $this->log($metadata,$event,"Starting action",$e);
                        foreach ($this->actionChannels[$e["id"]] as $callChannel) {
                                $this->createCallFile($callChannel);
                        }
                }

                //send email report
                if (is_array($this->actionChannelsMail[$e["id"]])) {
                        $msg ="Ademco alarm report.\n\n";
                        $msg.="Logged event at ".date("d.m.Y H:i:s",time()).".\n\n";
                        $msg."Event data:\n\n";
                        $msg.=implode(", ",$metadata)."\n";
                        $msg.=$event."\n";
                        $msg.=implode(", ",$e)."\n";
                        $msg.=$this->eventMap[$e["e"]]."\n\n";
                        $subject ="[ ALARM ] ".$this->eventMap[$e["e"]];
                        foreach ($this->actionChannelsMail[$e["id"]] as $email => $name) {
                                $this->sendMail($subject,$name,$email,$this->emailFromName,$this->emailFrom,$msg);
                        }
                }
        }

        function createCallFile($channel) {
                $callFile ="Channel: ".$channel."\n";
                $callFile.="CallerID: ".$this->callerId."\n";
                $callFile.="MaxRetries: 10\n";
                $callFile.="RetryTime: 60\n";
                $callFile.="WaitTime: 30\n";
                $callFile.="Context: alarmreport\n";
                $callFile.="Extension: start\n";
                $callFile.="Priority: 1\n";
                file_put_contents($this->callFileDir.uniqid("alarm").".call",$callFile);
        }

        function getSavedEvent () {
                $lastEvent=@file_get_contents($this->eventsDir.$this->lastEventSave);

                return $lastEvent;
        }

        function saveEvent($event) {
                file_put_contents($this->eventsDir.$this->lastEventSave,$event);
        }

        function parseEvent($event) {
                $e=array(
                        "id=> substr($event,0,4),
                        "q=> substr($event,4,3),
                        "e=> substr($event,7,3),
                        "z=> substr($event,10),
                );
                //print_r($e);

                return($e);  
        }

        function log($metadata,$event,$string,$e) {
                syslog(LOG_NOTICE,implode(", ",$metadata).", ".$event.", ".$string.", ".implode(", ",$e).", ".$this->eventMap[$e["e"]]);
        }

        function parse($file) {
                $fp=fopen($this->eventsDir.$file,"r");        
                $section="";  
                $data=array();
                while (!feof($fp)) {
                        $line = trim(fgets($fp,4096));
                        if (substr($line,0,1) == "[") {
                                //new section started
                                $section=substr($line,1,-1);
                                $data[$section]=array();
                        } elseif (strlen($section) > 0) {
                                //we are in section
                                switch ($section) {
                                        case "metadata":
                                                if (strlen($line) > 0) {
                                                        //dont include empty lines
                                                        list($key,$val)=explode("=",$line);
                                                        //echo $key."->".$val."\n";
                                                        $data[$section][trim($key)]=trim($val);
                                                }
                                        break;
                                        case "events":
                                                if (strlen($line) > 0) {
                                                        $data[$section][]=trim($line);
                                                }
                                        break;
                                }  
                        }
                        //echo $line."\n";  
                }
                fclose($fp);
                
                return $data;
        }

        function sendMail($subject,$tostr,$toemail,$fromstr,$fromemail,$msg) {
                $from = '"';
                $from.=$this->encode($fromstr);
                $from.= '"';
                $from.="<".$fromemail.">";

                $to = '"';
                $to.=$this->encode($tostr);
                $to.= '"';
                $to.="<".$toemail.">";

                //$msubject=$this->encode($subject);
                $msubject=$subject;

                $headers  = "MIME-Version: 1.0\n";
                $headers .= "Content-type: text/plain; charset=utf-8\n";
                $headers .= "From: ".$from."\n";

                return mail($to, $msubject, $msg, $headers);
        }

        function encode($in_str, $charset="UTF-8") {
                $out_str = $in_str;
                if ($out_str && $charset) {

                        $end = "?=";
                        $start = "=?. $charset . "?B?";
                        $spacer = $end . "\n . $start;

                        $length = 75 - strlen($start) - strlen($end);
                        $length = floor($length/2) * 2;

                        $out_str = base64_encode($out_str);
                        $out_str = chunk_split($out_str, $length, $spacer);

                        $spacer = preg_quote($spacer);
                        $out_str = preg_replace("/. $spacer . "$/", "", $out_str);
                        $out_str = $start . $out_str . $end;
                }

                return $out_str;
        }

}

$aep=new ademcoEventParser();
$aep->getEventFiles();
$aep->loop();

?>