Self-hosted push-to-talk for field operations.
Voice, GPS, emergency alerts, weather, dispatch, lone-worker shifts, and a SIP bridge — running on your hardware, talking to your radios.
Dashboard groups: Live Ops (Overview, Dispatch) for what's happening right now; Directory (Users, Channels) for the fleet roster; System (SIP Gateway, Dispatch Setup, Features, Admins, Audit Log, Call Log) for configuration and history. Each module's UI hides automatically when its feature is disabled in System → Features.
01 Voice Communication (PTT)
Voice Push-to-Talk
Two-way low-latency voice. Press the hardware PTT button to transmit, release to listen. Audio is encrypted in transit and routed by channel, so only members of the channel hear what's said.
Client: the openPTT app on Android — Hytera P50 ruggedised radios, or any Android device with a PTT button.
Voice PTT triple-tap gestures
Three quick PTT presses within 1.2 seconds trigger a special action. Currently:
• Triple-tap PTT → toggle lone-worker shift start/stop (see Lone Worker Safety).
Normal single-press PTT behaviour is unaffected — you can still talk normally.
• Orange top button → cycle presence (see Presence & Audibility). Not a PTT gesture, but it's the other common hardware action.
02 Channels
Voice Channel management
Create channels for different teams or purposes (Warehouse, Security, Office). Users join a channel to hear and talk to other members. Managed from Directory → Channels (Add form + per-row delete). The Channels stat card on the Live Ops Overview is also clickable as a shortcut.
Special channels:
• Emergency — Auto-created for SOS events. All users are moved here during emergencies.
• Weather — double-press PTT to hear a spoken weather report for your GPS location.
• Phone — reserved for inbound phone calls (see SIP Phone Gateway). Sub-channels under Phone are entered automatically when you accept a call; no need to navigate there manually.
Admin Call Groups — scoped channel access
Call Groups let you restrict a channel to a subset of users — the sales team, a single customer site, a duty roster. A channel without a group assignment stays visible to everyone (the default); a channel assigned to a group is reachable only by its members. Admin-role users bypass the restriction so they can always moderate.
Setting it up (Directory → Call Groups):
- Create a group (e.g. "Sales").
- Open the group, tick the members who should have access, and tick the channels the group owns. Save.
- Un-ticking a channel releases it back to visible-to-all; ticking it into a different group reassigns.
Two enforcement layers run side by side:
• Layer 1 — bounce on entry (always on): if a non-member ends up in a restricted channel they are moved out to Root within ~30 seconds. Used as a safety net.
• Layer 2 — hide from the channel tree (toggle in Features): the restricted channel is removed from the carousel and knob rotation on the P50 entirely. Non-members can’t see it, can’t scroll to it, and attempts to join are refused at the protocol level. This is the "enterprise" behaviour most teams want once things are running smoothly.
Pending pills in the member modal: every user shows a pill — pending cert (they’ve never connected yet, so we haven’t captured their Mumble cert hash), pending registration (cert captured but they’re not registered in Murmur’s sqlite yet — the scheduler catches them within 60 s), or registered (good to go, the ACL will allow them in). Pending users are locked out of hidden channels by design — they reconnect once and are fine.
Force all reconnect on the Call Groups tab restarts Murmur (~3 s of disruption) so every client re-reads the channel tree. Use it if something drifts — e.g. a user was registered mid-session and the group’s ACL was applied before they had a user id. Rarely needed in normal operation.
Rollout tip: Layer 2 requires every user to have connected at least once after deploying so the bridge can capture their Mumble cert hash. The Features tab toggle stays off by default; flip it once the pending cert pills in your groups are all cleared.
03 GPS Tracking
Ops Live device tracking
GPS reporting is built into the openPTT app — no extra installs on the radio. Worker positions appear on the embedded map under Live Ops → Dispatch. The Overview table also lists per-device GPS coordinates, battery level, and last-seen time.
Reporting cadence: every 30 seconds or 50 m of movement, whichever comes first. The radio's username is the same identifier used for tracking, so voice and location stay tied together automatically.
04 Emergency SOS
Safety Emergency alert system
When an SOS is triggered:
- All connected users are automatically moved to the Emergency channel
- Red alert banner appears on the admin dashboard with GPS location
- Webhook / email notification sent to configured admin contacts
- Everyone can hear the person in distress, regardless of which channel they were in
Acknowledge: an admin can acknowledge from the dashboard or by typing "OK" in the Emergency channel chat. All users are then moved back to their original channels.
How it triggers: from the dashboard, from the radio (man-down / panic button events on supported hardware route in automatically), or from any external system via webhook.
05 Weather Channel (ATIS)
Ops Spoken weather reports
Aviation-style weather reporting. Join the Weather channel and press PTT twice quickly (within 10 seconds). The system reads your GPS position, fetches the current conditions plus a 6-hour outlook, and plays a spoken report in the channel.
Report includes: temperature, wind speed and direction, cloud cover, current conditions, plus a 6-hour outlook mentioning expected rain and wind gusts when above a usable threshold.
Rate limit: one request per user per minute.
06 Dispatch
Ops Single-page dispatch workbench
The Live Ops → Dispatch page is the operator's whole workbench: quick-pick chips, search, an embedded map with live worker positions, the ranked nearest-worker list, and the recent-dispatch log — top to bottom on one screen.
Pick a target three ways:
- Click a saved-location chip at the top.
- Type a place name or
lat, lngin the search box and hit Find Nearest. - Click anywhere on the map.
An amber target pin drops on the map and the Nearest Workers table populates with the closest workers — capped and optionally radius-filtered per the limits set in Dispatch Setup. Distance is shown as straight-line metres.
Ops Sending a dispatch
Click Dispatch on a worker row to open the send modal. Pick a canned message from the dropdown (auto-fills the textarea, still editable) or type free-text. Press Send.
The message is delivered as a private spoken message directly to that worker — only they hear it, regardless of which channel they're in. If voice delivery fails for any reason, the message falls back to a text message in the worker's current channel.
The Recent Dispatches log at the bottom of the page shows the last 50 dispatches (newest first) with timestamp, target, message, and coordinates. Updates live after each send.
Admin Dispatch Setup
All dispatch configuration lives at System → Dispatch Setup on a separate page so the operational view stays uncluttered:
- Map Home — default map centre (latitude, longitude) and zoom level.
- Search Behaviour — max workers (1–50) caps the nearest-list length; search radius in metres (0 = unlimited) filters out workers further than that from the target.
- Saved Locations — add / delete the quick-pick chips. Accepts
lat, lngor a place name. - Canned Messages — pre-written dispatch text the operator picks from the dropdown. Lower Sort order values appear first.
Both the operational page and Setup page disappear from the dashboard when the dispatch module is turned off in Features.
Nearest-workers filter: only workers whose presence is Online and who are currently connected to the voice server are candidates. Busy, Offline, and never-set statuses are excluded — see Presence & Audibility.
07 Presence & Audibility
Voice Three-state presence on every radio
Every radio user has a presence state that operators can see at a glance and dispatch respects:
• Online — available for dispatch and calls.
• Busy — still on duty but do not dispatch right now.
• Offline — off-duty. Dispatch skips them.
Setting status from the radio: short-press the orange button on top of the P50. The state cycles Online → Busy → Offline → Online; the radio confirms each change by voice ("Busy" etc.) and the status pill under the active-channel name updates immediately.
Setting status from the dashboard: Directory → Users → Edit → Status drops a user to any of the three states. Useful when a worker goes home without signing off. Each change is recorded in the audit log with who set it and why.
Auto-Online on connect: when a radio first connects to the voice server (or reconnects after a dropout), the server promotes its user to Online automatically. Any prior Busy or Offline pick is replaced — re-pick it if you want it to stick.
Safety Shift interplay (lone-worker only)
When the Lone Worker feature is on and the user is marked as a lone worker:
• Starting a shift (triple-tap PTT) force-sets the user's status to Online, overriding Busy.
• Setting Offline (orange button or dashboard) ends the active shift with reason user_offline and is recorded in the audit trail.
Outside lone-worker mode, presence is a pure intent signal — no shift side-effects.
Ops Device audibility
The radio also reports whether its device is audible — ringer on normal and voice-call stream volume above zero. If the device is silent, in vibrate mode, or has voice volume at 0:
• A muted icon 🔇 shows next to the user's pill in Live Ops → Overview and Directory → Users.
• TTS dispatches and ring signals won't be heard — the operator can spot this before sending a whisper.
Audibility is captured every time the user presses the orange button and on each Mumble reconnect. Quick mute/unmute that isn't followed by an orange-press may take up to the next status cycle to refresh.
08 Lone Worker Safety
Safety Shift-based monitoring
Lone-worker check-ins are scoped to explicit shifts. When a user starts a shift they're monitored; when the shift ends (manually or at the planned end time), monitoring stops. No 24/7 pings.
Start / stop a shift from the radio: triple-tap the PTT button (three presses within 1.2 s). The radio confirms by voice:
• First triple-tap: "Shift started. Ends at HH:MM."
• Second triple-tap: "Ending shift. Long press again to cancel." — three-second grace window; triple-tap again to cancel, or let it elapse to commit the end.
Shift duration is set per user on the user edit form (1–24 h). Leave it blank to use the deployment-wide default.
While a shift is active:
• A check-in reminder fires every minute when overdue: "Automated check-in for {name}, please respond."
• Shifts past their planned end time auto-close.
• Status badge on the Users tab: OK / Due soon / OVERDUE
09 SIP Phone Gateway
Ops Inbound phone calls on the radio
Inbound phone calls land on the radio fleet via the SIP gateway. Each concurrent call gets its own dedicated sub-channel; the configured concurrency cap is the maximum number of simultaneous calls (callers beyond the cap get a busy tone).
Who can answer: users with the Can answer SIP calls permission. Non-eligible users can't enter the call sub-channels.
Notification: every eligible user hears a private ringing tone and sees a full-screen incoming-call overlay on their radio with Answer and Decline buttons. Tapping Answer moves the user straight into the live call.
Radio-side call control while in an active call:
• Green button — toggle caller-mute. Other radios on the call still hear each other; the caller hears silence. Press again to restore audio.
• MENU key — hang up. The radio confirms with a spoken "call ended."
• The channel knob is locked during an active call so a stray rotation can't move you out mid-conversation.
Editable greeting: the spoken greeting played to callers before they're connected is editable per trunk on System → SIP Gateway. Save & Regenerate updates it live.
10 User Management
Admin Device user accounts
Create users for each device from the Directory → Users tab. Each user gets a username, password, and optional display name. Voice and GPS share the same identity automatically.
QR code: click QR on a user row to generate a QR code for quick manual device enrolment. Scan with the radio's camera to auto-configure the app.
Roles and permissions:
- Admin — can acknowledge SOS from the radio (type "OK" in the Emergency channel).
- Lone Worker — subject to shift-based check-in monitoring. When checked, also set the per-user shift duration (1–24 h) or leave blank for the deployment-wide default.
- Can answer SIP calls — permission to take inbound phone calls.
- User — standard radio user, no extra privileges.
Reset device registration: if a radio's identity has gone stale, the user edit form has a one-click reset that re-issues the credentials on the radio's next connection (briefly interrupts the voice server while it reloads).
11 Admin Accounts
Admin Dashboard administrators
Manage admin dashboard accounts from System → Admins. Passwords are stored hashed using a modern slow-hash algorithm.
Roles: Admin (full access) or Viewer (read-only dashboard).
12 Device Provisioning
The provisioning link runs on your laptop, not on the P50.
Plug the P50 in over USB with USB debugging on. The script drives adb from your Mac or PC to install the APK and seed credentials on the handset. The P50 has no keyboard and no way to run shell scripts — it's the target, not the executor.
Admin One-click provisioning link
From the user edit form → Generate provisioning link. The dashboard shows a short URL plus a QR code. Send the URL to the tech doing the install.
What the tech does on their laptop:
- Plug the P50 in via USB. Enable Developer Options → USB debugging. Accept the RSA fingerprint prompt on the handset.
- Open the short link in a browser; it downloads
openptt-provision.sh(macOS / Linux) oropenptt-provision.ps1(Windows). - Run the script:
# macOS / Linux curl -fsSL https://ptt.harro.ch/p/<slug> -o setup.sh bash setup.sh
# Windows (PowerShell) Invoke-WebRequest https://ptt.harro.ch/p/<slug> -OutFile setup.ps1 .\setup.ps1
What the script does, unattended:
- Verifies exactly one radio is connected over USB.
- Installs the signed openPTT app.
- Disables any pre-installed manufacturer PTT client that would conflict with openPTT (reversible).
- Grants the runtime permissions the app needs (microphone, location).
- Whitelists the app from background-power restrictions so it stays connected with the screen off.
- Seeds the radio with the operator's voice credentials and server addresses, then auto-launches the app.
Prerequisites on the laptop:
adbin PATH (brew install --cask android-platform-toolson macOS; Android platform-tools on Windows).sqlite3in PATH (ships on macOS and most Linux;winget install SQLite.SQLiteon Windows).
Token lifetime: 24 hours, single-use. The token is marked used when the script finishes successfully. Generate a new one for each device.
Idempotent: if a step errors, re-run the script — every step is safe to repeat. If the error persists, capture it and ping an admin.
13 Bulk Enrollment
Admin CSV upload
From the Admin tab, upload a CSV file to create multiple users at once. Download the template, fill in usernames and passwords, upload. Returns a ZIP file with QR codes for each device.
For unattended provisioning of new devices, prefer the per-device provisioning link flow — it seeds credentials and launches the app without the tech having to type anything into the P50.
14 Audit Log
Admin Action history
System → Audit Log shows every admin action: user creation and deletion, admin management, SOS events, presence changes (with before/after pills and source — self / admin / auto_connect / shift_start / shift_stop_offline), and shift stops. Filterable by action type. Includes timestamp, admin username, and IP address.
System → Call Log is the phone-call equivalent — every inbound call's start time, who answered (if anyone), and end time, with caller ID.
15 Feature toggles
Admin Enable / disable modules deployment-wide
System → Features exposes one switch per module: Lone Worker, SIP, Dispatch, Weather, SOS. Disabling a module:
- Hides the module's tabs and inline UI from the dashboard immediately.
- Stops the module's background processes from running.
- Hides the matching UI on the radios (the radios pick up the change within ten minutes; force a faster refresh by restarting the radio app).
Data is preserved — re-enabling restores full behaviour. SOS shows a confirmation dialog before disabling because turning it off removes emergency response from the system.
16 Monitoring
Admin Health and metrics endpoints
Two HTTP endpoints are exposed for external monitoring tools:
GET /metrics— Prometheus-compatible counters and gauges (uptime, voice-server status, online and total users, channels, SOS events).GET /api/status/health— lightweight JSON health probe.
17 Manual Device Setup
When the provisioning link isn't available
For most devices, use the one-click provisioning link. The manual path below is only for first-of-its-kind testing or recovery.
| App | Purpose | Server | Port |
|---|---|---|---|
| openPTT | Voice PTT + GPS + shift control | voice.harro.ch | 443 (voice) / 5055 (GPS) |
Sideload via adb:
adb install -r openptt-foss-release.apk
First-time settings in the app:
- Voice server:
voice.harro.ch:443with the user's voice credentials. - GPS tracking server:
http://voice.harro.ch:5055. - Admin server:
https://ptt.harro.ch(required for triple-tap shift control). - Grant the microphone and location runtime permissions when prompted.
Device settings: disable battery optimisation for openPTT and enable background running. The app then auto-connects on every boot.
Hardware buttons on the Hytera P50:
- Side PTT button: push to talk.
- Triple-tap PTT within 1.2 s: toggle a lone-worker shift.
- Channel knob: rotate left or right to switch channels.
- Orange top button: cycle presence (Online → Busy → Offline → Online) — see Presence & Audibility.
- Green button: mute toggle during an active phone call.
- MENU key: hang up during an active phone call.
- Red / F3 keys: owned by the radio's firmware, not used by openPTT.
18 Network & Ports
Server ports
| Port | Protocol | Service |
|---|---|---|
| 443 | TCP | Voice + HTTPS admin dashboard |
| 80 | TCP | HTTP redirect to HTTPS |
| 64738 | TCP + UDP | Direct voice fallback |
| 5055 | TCP + UDP | GPS tracking |
| 5060 | UDP | Inbound SIP phone calls |