5.3 KiB
Runtime Control — Start/Stop Counting
The counter can pause and resume object detection & counting on the fly, without restarting the process. There are three interchangeable ways to control it, and they all converge on a single source of truth: the control file.
- Control file — a small JSON file the counter polls.
- TCP control socket — line commands over the network that update the file.
- Dashboard button — a COUNTING ON/OFF toggle that writes the file via its API.
When counting is OFF, the counter skips inference entirely (no detection, no
counting, lower CPU/NPU load), the video/live stream keeps running, and a
COUNTING PAUSED badge is drawn on the frame. When ON, normal counting
resumes.
1. Enable runtime control
Runtime control is opt-in. In your .env:
# Master switch — required for ALL control methods (file, socket, dashboard).
CONTROL_ENABLED=true
# Shared control file. MUST be identical for the counter and the dashboard.
CONTROL_FILE=/opt/zenai-ktc-counter/control.json
# Counting state on startup / when the control file does not exist yet.
CONTROL_DEFAULT_COUNTING=true
# How often (seconds) the counter re-reads the control file.
CONTROL_POLL_SEC=1.0
When CONTROL_ENABLED=false, the counter always counts, the control file is
ignored, and the dashboard hides the toggle button.
Changes take effect within
CONTROL_POLL_SECseconds (default 1s), because the counter re-reads the file on a timer.
2. Control file
Format
{ "counting": true }
"counting": true→ counting ON"counting": false→ counting OFF (paused)
The counter creates this file on startup (seeded from CONTROL_DEFAULT_COUNTING)
if it does not exist. All writers (counter, dashboard, socket) write it
atomically (temp file + rename), so readers never see a half-written file.
Toggle by editing the file
Pause counting:
printf '{"counting": false}\n' > /opt/bytetrack-counter/control.json
Resume counting:
printf '{"counting": true}\n' > /opt/bytetrack-counter/control.json
Check current state:
cat /opt/bytetrack-counter/control.json
Use the exact path from your
CONTROL_FILEsetting. If you write it by hand, keep it valid JSON — an unreadable file falls back toCONTROL_DEFAULT_COUNTING.
3. TCP control socket
The socket lets you toggle counting over the network. It updates the same control
file, so changes still apply within CONTROL_POLL_SEC.
Enable
# Requires CONTROL_ENABLED=true as well.
CONTROL_SOCKET_ENABLED=true
# 127.0.0.1 = local only. Use 0.0.0.0 to allow remote clients.
CONTROL_SOCKET_HOST=127.0.0.1
# TCP port.
CONTROL_SOCKET_PORT=5090
Commands
Newline-terminated, case-insensitive. One connection can send multiple commands.
| Command | Effect | Reply |
|---|---|---|
START / RESUME / ON |
Counting ON | OK counting=on |
STOP / PAUSE / OFF |
Counting OFF | OK counting=off |
TOGGLE |
Flip current state | OK counting=on/off |
STATUS / GET |
Report state (no change) | OK counting=on/off |
| (anything else) | — | ERR unknown command |
Examples
Using nc (netcat):
printf 'STOP\n' | nc 127.0.0.1 5090
printf 'START\n' | nc 127.0.0.1 5090
printf 'TOGGLE\n' | nc 127.0.0.1 5090
printf 'STATUS\n' | nc 127.0.0.1 5090
Using bash /dev/tcp (no netcat needed):
exec 3<>/dev/tcp/127.0.0.1/5090
printf 'STATUS\n' >&3
head -n1 <&3
exec 3>&-
Python client:
import socket
def control(cmd, host="127.0.0.1", port=5090):
with socket.create_connection((host, port), timeout=2) as s:
s.sendall((cmd + "\n").encode())
return s.recv(256).decode().strip()
print(control("STATUS")) # OK counting=on
print(control("STOP")) # OK counting=off
Security: the socket has no authentication. Keep
CONTROL_SOCKET_HOSTon127.0.0.1, or restrict access with a firewall / trusted network if you bind to0.0.0.0.
4. Dashboard button
When CONTROL_ENABLED=true, the dashboard header shows a COUNTING ON/OFF
button (green when on, red when off). Clicking it flips the state immediately.
The dashboard must point at the same CONTROL_FILE as the counter (set it in
the dashboard's environment too). The dashboard exposes:
GET /api/control→{ "enabled": true, "counting": true }POST /api/controlwith body{ "counting": false }→ writes the control file (returns403ifCONTROL_ENABLED=false)
Notes & behavior
- Single source of truth: the socket and dashboard both write the control file; the counter reacts only to the file. This avoids race conditions between control methods.
- Latency: expect up to
CONTROL_POLL_SEC(default 1s) between issuing a command and the counter reacting. - Live stream keeps running while paused, so you still see the camera feed
with the
COUNTING PAUSEDoverlay. - Shared path requirement: counter and dashboard must use the same
CONTROL_FILE. If they run on different machines, use the TCP socket (or a shared network path) instead.