Lost Post: One Call, Several Actions for Lockdown

Edit: @penguinpbx, just FYI, the original post was placed in FreePBX > Configuration because the application can be used in any environment where they may call a lockdown, not just schools. This could apply to office buildings as well.

A week or so ago I had posted a message about needing to dial an extension on a phone and have it do multiple things. The post was unfortunately lost in the Great Forum Upgrade of 2025. TLDR, I got everything to work the way I wanted to, and then some. It required a mix of custom contexts, PHP scripting, and a lot of trial and error. I’m posting this here in the event someone was following that topic and curious how we did it.


I am looking for a single call to perform the following actions:

  • Call our Algo 8301 Paging Adapter to play an automated Lockdown announcement in the school
  • Call the local police station to inform them that the school has entered a lockdown
  • Activate strobe lights around the school

I finally had time to sit and work on this and wanted to get a handle on options from the community. I apologize as I don’t recall who said what but one person correctly stated that it is illegal for an automated call to go to 911 (we will be using the police station’s main number for this purpose). And other posts started to point me in the direction of using .call files.

After a bit of educating myself and much Googling, here’s the end result.

Created custom contexts in extensions_custom.conf

[lockdown-trigger]
exten => s,1,noop(Lockdown triggered in extensions_custom.conf)
same => n,System(php /var/lib/asterisk/agi-bin/lockdown.php ${CUT(CUT(PJSIP_HEADER(read,To),@,1),:,2)})
same => n,Playback(activated)
same => n,Hangup

[lockdown-strobes-off]
exten => s,1,noop(Lockdown strobe light deactivation routine called in extensions_custom.conf)
same => n,System(php /var/lib/asterisk/agi-bin/lockdown-strobesoff.php ${CUT(CUT(PJSIP_HEADER(read,To),@,1),:,2)})
same => n,Playback(ha/lights&off&silence/1&goodbye)
same => n,Hangup()

When one of the custom contexts are triggered, it’ll call a PHP script I wrote (script is below). The parameter being passed in ${CUT(CUT(PJSIP_HEADER(read,To),@,1),:,2)} just grabs the extension that was dialed since I have four buildings and only wanted to write this script once. This means the script will see what was dialed, and know what school is triggering the lockdown. And then a confirmation is played back before hanging up. If not evident, the [lockdown-trigger] initiates the lockdown where the [lockdown-strobes-off] turns off the strobe lights once the lockdown has ended.

Create Custom Desinations
I added two custom destinations to take advantage of these now created custom contexts.

Create Misc Applications
I added eight Misc Applications in order to allow each school to activate and deactivate a lockdown using a code dialed into the phone:

Create PHP Scripts
I made two PHP scripts that actually do the work. They were placed in /var/lib/asterisk/agi-bin/. One handles initialization of the lockdown, the other handles turning off the strobe lights once the lockdown ends. In the initialization script, we:

  • Check to see the incoming parameter (the dialed extension) to determine what building is being locked down.
  • Create a .call file to call the local police station and play a pre-recorded message as to which school has entered a lockdown.
  • Create a .call file to call the paging adapter and announce the lockdown.
  • Create a .call file to page the other school administrators informing them of the school that’s entered a lockdown. This was not originally planned, but was something requested after my initial scope was defined.
  • Open a curl connection to a “ControlByWeb” relay that triggers the lockdown strobe lights to turn on.

Names, numbers, and IP addresses have been altered to protect the innocent.

<?php
if (!isset($argv[1])) {
	// We have no known destination, bail
	exit;
}

// Pull the second digit - this tells us the destination (ex: 123 returns 2)
$dest = substr($argv[1], 1, 1);

switch ($dest) {
	case 1:
		// School 1
		$cid = '5558675309'; // Outbound caller ID
		$pdm = 'S1';         // School abbreviation used in grabbing what announcement we want to use
		$ips = '16';         // IP address prefix for the strobe lights
		break;
	case 2:
		// School 2
		$cid = '5558675310';
		$pdm = 'S2';
		$ips = '32';
		break;
	case 3:
		// School 3
		$cid = '5558675311';
		$pdm = 'S3';
		$ips = '48';
		break;
	case 4:
		// School 4
		$cid = '5558675312';
		$pdm = 'S4';
		$ips = '64';
		break;
	default:
		exit;
}

// This creates the call file that calls the police department
$callfile_police = '
Channel: PJSIP/5555555555@PJMasksip
Callerid: '.$cid.'
Application: Playback
Data: custom/'.$pdm.'_Lockdown_Police_Call&silence/1&custom/'.$pdm.'_Lockdown_Police_Call
MaxRetries: 3
RetryTime: 300
WaitTime: 60';
file_put_contents('/var/spool/asterisk/outgoing/'.$pdm.'_pd_'.time().'.call', $callfile_police);

// This creates the call file that announces the lockdown over the PA speakers
$callfile_pasystem = '
Channel: PJSIP/'.$dest.'55
Callerid: '.$cid.'
Application: Playback
Data: custom/Lockdown_Message&silence/2&custom/Lockdown_Message
MaxRetries: 1
RetryTime: 300
WaitTime: 60';
file_put_contents('/var/spool/asterisk/outgoing/'.$pdm.'_pa_'.time().'.call', $callfile_pasystem);

// This creates the call file that announces the lockdown to the administrators via the Emergency Call Notification Groups
$callfile_pasystem = '
Channel: PJSIP/'.$dest.'00
Callerid: '.$cid.'
Application: Playback
Data: custom/'.$pdm.'_Lockdown_Police_Call
MaxRetries: 1
RetryTime: 300
WaitTime: 60';
file_put_contents('/var/spool/asterisk/outgoing/'.$pdm.'_notify_'.time().'.call', $callfile_pasystem);

// This activates the lockdown strobe lights
$ch = curl_init('http://10.0.'.$ips.'.42/state.xml?relayState=1');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/xml']);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERPWD, ':supersecretpasswordhere');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_exec($ch);
curl_close($ch);
?>

This is a lot of PHP code for those that might not know what that is (not sure how many FreePBX administrators also program in PHP). For those that do know, note that I didn’t include the PHP shebang at the top of the script as I found in almost every example I came across. Not sure if things were from “long ago” but I found it didn’t work, and instead just had the System call in the custom context call the “php” executable from there.

same => n,System(php /var/lib/asterisk/agi-bin/lockdown.php ${CUT(CUT(PJSIP_HEADER(read,To),@,1),:,2)})

One final note - after discussing the procedures with our local police station (which if anyone is looking to replicate this, I highly suggest), we decided that after the lockdown is initiated, and the staff member is in a safe location, they still call 911 so they can relay additional information as needed.

2 Likes

This topic was automatically closed 31 days after the last reply. New replies are no longer allowed.