Summary
One Frogman command broadcasts a pre-recorded message to every Sangoma / DPMA P-Series phone listening on a named multicast paging zone. Phones auto-answer and play the recording at full volume regardless of state (idle, on-call — via interrupt level). Designed for K-12 lockdown / shelter-in-place / fire announcements where SIP unicast paging cannot scale.
fm_sangoma_emergency_alertconfirm: true)PushLockdown.php + PushSangomaZone.php (orphans removed; had flite/sox shell-outs)How it works
Architecture flow
What the tool does, step by step
- Resolve recording. Accepts a System Recording id or display name. Looks it up via
\FreePBX::Recordings()->getAllRecordings()and pulls the filename (e.g.custom/ldown). - Resolve zone. Reads the named zone from the EPM
endpoint_multicasttable. Defaults toLOCKDOWN. Returns the IP, port, codec, priority, and interrupt level. - Dry-run preview. Without
confirm: true, returns a structured preview showing exactly what would happen. - Fire. With
confirm: true, issues an AMIOriginate:Channel: MulticastRTP/basic/<ip>:<port> Application: Playback Data: <recording filename> Async: true - Asterisk emits the RTP stream to the configured multicast group. P-Series phones whose DPMA-provisioned config subscribes them to that group auto-join the IGMP group on boot, hear the stream, and play it.
Where the parts live
| Piece | Lives in | Managed by |
|---|---|---|
| Multicast zones (IP/port/codec) | endpoint_multicast DB table | Human, via FreePBX EPM GUI |
| Phone subscription (which zones to listen on) | /etc/asterisk/dpma/phone_configs/<ext>-1/<MAC>.cfg | EPM template → DPMA, on rebuild |
| System recordings | recordings DB table + /var/lib/asterisk/sounds/<lang>/custom/ | Human, via FreePBX System Recordings GUI |
| Send channel driver | chan_multicast_rtp (built into Asterisk) | Asterisk core |
| Frogman tool | Tools/SangomaEmergencyAlert.php | Frogman |
Why LAN-only
Multicast (224.0.0.0/4) is a network-layer feature designed for one-to-many delivery on a shared broadcast domain. Three independent reasons it cannot work over a WAN / cloud setup:
- ISPs do not route multicast. Default consumer and cloud-provider routing tables drop 239.x.x.x packets. Multicast routing (PIM, MSDP) requires explicit configuration that no commercial ISP offers to standard customers.
- Multicast does not survive NAT. The 1:N delivery model has no concept of source/destination port translation. Carrier-grade NAT, home routers, and SBCs all drop or fail to forward multicast.
- Phones must be in the same IGMP-aware L2 segment. A managed switch with IGMP snooping forwards multicast to subscribed ports; without that, the multicast group either floods or is silently dropped depending on the switch.
\FreePBX::Paging() module) is the only option, and it caps out around tens of phones before SIP fanout overhead becomes painful.
One-time setup (human, via FreePBX GUI)
Frogman cannot create multicast zones — there is no public BMO setter and CLAUDE.md rule 3 forbids writing to other modules' tables. This is a single-step setup per zone, done once per template.
- FreePBX admin → Connectivity → Endpoint Manager
- Open the relevant template (e.g.
digium_defaultfor Sangoma P-Series) - Multicast tab → Add Zone
- Recommended values for
LOCKDOWN:NameLOCKDOWNIP address239.255.0.1(site-local multicast range)Port10000CodecPCMU(G.711 µ-law — universally supported)Priority1Interrupt level2(interrupts active calls) - Save, then Rebuild Configs
- Phones reload on next DPMA poll, or run
asterisk -rx "digium_phones reconfigure phone <ext>-1"to force immediately - Optionally add additional zones:
FIRE(239.255.0.2),SHELTER(239.255.0.3),ALL_CLEAR(239.255.0.4) on subsequent ports
Then create your System Recording: Admin → System Recordings → Add Recording. Record a short clear message (8–15 seconds typical for a lockdown announcement).
Commands
Chat (preferred)
list multicast zones
emergency alert <recording>
emergency alert <recording> on zone <zone>
lockdown <recording>
CLI
# list zones
fwconsole frogman:tool fm_list_sangoma_multicast_zones '{}'
# dry run (preview)
fwconsole frogman:tool fm_sangoma_emergency_alert '{"recording":"Lockdown"}'
# fire (with confirm)
fwconsole frogman:tool fm_sangoma_emergency_alert '{"recording":"Lockdown","confirm":true}'
# fire to a non-default zone
fwconsole frogman:tool fm_sangoma_emergency_alert \
'{"recording":"Fire Alarm","zone":"FIRE","confirm":true}'
HTTP / MCP
Tool auto-registers in the Frogman HTTP catalog and MCP server — same parameter shape as the CLI invocations above.
Diagnostic CLI (Asterisk)
# confirm channel driver loaded
asterisk -rx "core show channeltypes" | grep MulticastRTP
# verify what DPMA wrote to a phone's config
grep -A2 multicastpage /etc/asterisk/dpma/phone_configs/101-1/*.cfg
# force a phone to reload its config
asterisk -rx "digium_phones reconfigure phone 101-1"
# direct-fire test (bypasses Frogman, uses stock sound)
asterisk -rx "channel originate MulticastRTP/basic/239.255.0.1:10000 \
application Playback demo-congrats"
In-walls routing analysis
CLAUDE.md hierarchy: BMO → AMI → GraphQL → direct DB read. Each piece of this tool maps to a documented surface; no shell-outs, no fwconsole, no writes to another module's tables.
| Action | Surface | Rule |
|---|---|---|
| List system recordings | \FreePBX::Recordings()->getAllRecordings() | BMO Rule 1 — in walls |
| Resolve recording filename | \FreePBX::Recordings()->getFilenameById() | BMO Rule 1 — in walls |
| Originate multicast call | $astman->Originate(...) | AMI Rule 2 — in walls |
| Read multicast zones | SELECT FROM endpoint_multicast | DB read Rule 4 — documented exception (no BMO surface) |
endpoint_multicast — the GUI is the only writer; everything else is client-side JavaScript. Frogman never writes to this table.
PushSangomaZone.php shelled out to flite and sox for TTS rendering. Both shell-outs were flagged by the wall-audit. The new tool drops TTS entirely — emergency announcements should be pre-recorded for clarity and brand consistency anyway.
Troubleshooting checklist
If the dry-run preview is correct but phones stay silent, work through this list:
- Are PBX and phones on the same LAN? Compare
ip -4 addron the PBX with the contact IP fromasterisk -rx "pjsip show contacts". If different public IPs, multicast cannot reach them. (This is the dev box state.) - Is the channel driver loaded?
asterisk -rx "core show channeltypes" | grep MulticastRTP. Should showMulticastRTP — Multicast RTP Paging Channel Driver. - Did the channel actually go up? Right after fire, run
asterisk -rx "core show channels". You should see aMulticastRTP/<ip>:<port>channel inUpstate runningPlayback(...). - Does the phone's config include the zone?
grep -A2 multicastpage /etc/asterisk/dpma/phone_configs/<ext>-1/*.cfg. If the<multicastpage>block is empty, EPM did not push the zone — revisit the template's Multicast tab and Rebuild Configs. - Has the phone reloaded since the zone was added?
asterisk -rx "digium_phones reconfigure phone <ext>-1"sends an immediate reconfigure message. Without it, phones pick up the new config on next DPMA poll (minutes). - Is the LAN switch IGMP-aware? Some unmanaged switches flood multicast (works but loud); some managed switches with IGMP snooping but no querier silently drop it.
tcpdump -i <iface> host 239.255.0.1on the phone-side network confirms whether packets arrive. - Codec mismatch? Recording must transcode to PCMU.
soxi /var/lib/asterisk/sounds/en/custom/<file>.wav— 8 kHz mono works directly; higher rates transcode automatically; weird codecs may fail silently.
Limitations & next steps
Current limitations
- LAN-only (architectural; not fixable in code).
- Sangoma / DPMA P-Series only. Yealink / Polycom / Grandstream multicast subscriptions need a separate EPM template tab and are not currently exposed by this tool. Cross-brand support is a future build.
- One-shot fire. No "ongoing" multicast stream, no scheduled drills, no all-clear chaining yet. Each invocation plays one recording end-to-end.
- No screen takeover. Audio only. DPMA can push a "Lockdown" overlay via SIP NOTIFY but that's a separate composition layered on top — future build.
Roadmap candidates
- Flic2 button integration. Wireless physical panic buttons (flic.io/flic2). Trigger
fm_sangoma_emergency_alertvia Flic Hub HTTP webhook → Frogman API token → instant lockdown without dialing. Natural fit for classroom deployment. - Phone App soft-key panic button. Sangoma-only XML app pinned to a soft key on every P-Series phone. Single press, optional confirm screen, fires the same tool.
- Scheduled drills. Cron-style schedule using the existing Frogman scheduling surface to drive monthly fire / lockdown drills with a different "drill" recording.
- SIP NOTIFY screen takeover. Layer
PJSIPNotifyAMI action on top to push a lockdown screen overlay alongside the audio. - All-clear chaining. A second tool
fm_sangoma_all_clearthat fires the all-clear recording on the same zone and clears any screen overlay.