Files
2026-07-11 17:19:14 +07:00

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/bytetrack-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_SEC seconds (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_FILE setting. If you write it by hand, keep it valid JSON — an unreadable file falls back to CONTROL_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_HOST on 127.0.0.1, or restrict access with a firewall / trusted network if you bind to 0.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/control with body { "counting": false } → writes the control file (returns 403 if CONTROL_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 PAUSED overlay.
  • 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.