#!/usr/bin/php -q
<?php
/*
Simple Alarm call center script. This script can by used as after script for Asterisks AlarmReceiver cmd. 

This script read saved events from asterisk alarm receiver. If the event is important (Alarm event) script create 
asterisks 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 was -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 Asterisks AlarmReceiver cmd can be found here:
http://www.voip-info.org/wiki/index.php?page=Asterisk+cmd+AlarmReceiver

------------------------------------------------------------------------------------------------
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()


------------------------------------------------------------------------------------------------

Written by
Uros Indihar <uros.indihar@alphito.si>
Alphito d.o.o.

Changelog 
	0.1 Initial release
	0.2 1.12.1008
		Added mail support

License GNU GPL2.

Warranty: None. Use at your own risk !

*/
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();

?>