update webhook service dan API untuk webhook control web dapat status json sekarang

This commit is contained in:
Alberto-Audrix committed 2026-08-14 16:29:58 +07:00
1 parent c80984ba80
commit b52e876eed
3 files changed
+90 -18

No files matched your search

+28 -11
View File
@@ -4,10 +4,11 @@ Production deployment for **RK3588** (or compatible RKNN NPU) edge devices runni
| Component | Script | systemd unit |
|-----------|--------|--------------|
| Status webhook (IN/OUT/OFF gate) | `status_webhook.py` | `zenai-kpc-status-webhook.service` |
| RTSP counter (RKNN + ByteTrack) | `counter_live_rknn.py` | `zenai-kpc-counter.service` |
| Web dashboard (Flask) | `counter_dashboard.py` | `zenai-kpc-dashboard.service` |
Both processes share a single `.env` file and read/write the same SQLite database and state JSON.
All three processes share a single `.env` file. The counter and dashboard read/write the same SQLite database and state JSON. The webhook writes `status_webhook_state.json`, which the counter polls when `STATUS_WEBHOOK_ENABLED=true`.
---
@@ -23,7 +24,7 @@ Both processes share a single `.env` file and read/write the same SQLite databas
```bash
sudo apt update
sudo apt install -y python3 python3-venv python3-pip ffmpeg libgl1
sudo apt install -y python3 python3-venv python3-pip ffmpeg libgl1 openssl
```
`ffmpeg` is required for low-latency RTSP capture via OpenCV and for session MP4 recording (`RECORD_VIDEO=true`). `libgl1` is often needed for `opencv-python` on headless systems.
@@ -49,6 +50,7 @@ Default paths used by the service files and `env.example`:
├── counter_live_rknn.py
├── counter_dashboard.py
├── counter_store.py
├── status_webhook.py
├── templates/
├── venv/ # Python virtual environment (created during install)
├── .env # runtime config (not in git)
@@ -123,21 +125,23 @@ sudo mkdir -p /opt/zenai-kpc-counter /opt/models /dev/shm/zenai-kpc-counter
```bash
cd /opt/zenai-kpc-python
sudo cp zenai-kpc-counter.service zenai-kpc-dashboard.service /etc/systemd/system/
sudo cp zenai-kpc-status-webhook.service zenai-kpc-counter.service zenai-kpc-dashboard.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable zenai-kpc-counter zenai-kpc-dashboard
sudo systemctl enable zenai-kpc-status-webhook zenai-kpc-counter zenai-kpc-dashboard
sudo systemctl start zenai-kpc-status-webhook
sudo systemctl start zenai-kpc-counter
sudo systemctl start zenai-kpc-dashboard
```
The dashboard unit starts **after** the counter unit (`After=zenai-kpc-counter.service`).
The dashboard unit starts **after** the counter unit (`After=zenai-kpc-counter.service`). Enable the webhook so it comes up on boot with the other two.
### Verify
```bash
systemctl status zenai-kpc-status-webhook
systemctl status zenai-kpc-counter
systemctl status zenai-kpc-dashboard
journalctl -u zenai-kpc-counter -f
journalctl -u zenai-kpc-status-webhook -f
```
Open the dashboard in a browser:
@@ -148,6 +152,14 @@ http://<device-ip>:5000
(Port is set by `DASHBOARD_PORT` in `.env`, default `5000`.)
Status webhook endpoints (fixed ports):
```text
http://<device-ip>:8002/?lokasi=IN|OUT&status=ON|OFF
https://<device-ip>:8443/?lokasi=IN|OUT&status=ON|OFF
http://<device-ip>:8002/status
```
---
## 5. Operations
@@ -155,6 +167,7 @@ http://<device-ip>:5000
### Restart after config change
```bash
sudo systemctl restart zenai-kpc-status-webhook
sudo systemctl restart zenai-kpc-counter
sudo systemctl restart zenai-kpc-dashboard
```
@@ -162,6 +175,7 @@ sudo systemctl restart zenai-kpc-dashboard
### View logs
```bash
journalctl -u zenai-kpc-status-webhook -n 100 --no-pager
journalctl -u zenai-kpc-counter -n 100 --no-pager
journalctl -u zenai-kpc-dashboard -n 100 --no-pager
```
@@ -169,7 +183,7 @@ journalctl -u zenai-kpc-dashboard -n 100 --no-pager
### Stop services
```bash
sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter
sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter zenai-kpc-status-webhook
```
The counter handles `SIGTERM` gracefully — it finishes the current frame, persists state to SQLite, then exits.
@@ -178,10 +192,10 @@ The counter handles `SIGTERM` gracefully — it finishes the current frame, pers
```bash
cd /opt/zenai-kpc-python
sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter
sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter zenai-kpc-status-webhook
# rsync or git pull new code
sudo ./venv/bin/pip install -r requirements.txt # if dependencies changed
sudo systemctl start zenai-kpc-counter zenai-kpc-dashboard
sudo systemctl start zenai-kpc-status-webhook zenai-kpc-counter zenai-kpc-dashboard
```
---
@@ -191,8 +205,10 @@ sudo systemctl start zenai-kpc-counter zenai-kpc-dashboard
| Symptom | Things to check |
|---------|-----------------|
| Counter won't start | `journalctl -u zenai-kpc-counter`; verify `MODEL_PATH` exists; RKNN drivers installed |
| Status webhook won't start | `journalctl -u zenai-kpc-status-webhook`; ports 8002/8443 free; `openssl` installed if certs are missing |
| No RTSP frames | Ping camera; test with `ffplay <SOURCE>`; check `OPENCV_FFMPEG_CAPTURE_OPTIONS` |
| Dashboard shows 0 count | `STATE_FILE` in `.env` must match between counter and dashboard; check file exists |
| Counting stays OFF | Check `STATUS_WEBHOOK_ENABLED` and `GET http://<device-ip>:8002/status`; `STATUS_WEBHOOK_FILE` must match the webhook state file |
| Live video blank | `LIVE_STREAM_ENABLED=true`; path matches `LIVE_STREAM_FRAME_PATH` in both processes; if using nginx, set `proxy_read_timeout` (see §7) |
| Wrong counts | Tune `LINE_Y1_FRAC`/`LINE_Y2_FRAC`, `CONF`, ByteTrack thresholds; enable `DEBUG_TRACKING=true` temporarily |
| Service keeps restarting | `journalctl -u zenai-kpc-counter -e`; often missing model, bad RTSP URL, or venv not created |
@@ -202,8 +218,9 @@ sudo systemctl start zenai-kpc-counter zenai-kpc-dashboard
```bash
cd /opt/zenai-kpc-python
source venv/bin/activate
python counter_live_rknn.py # terminal 1
python counter_dashboard.py # terminal 2
python status_webhook.py # terminal 1
python counter_live_rknn.py # terminal 2
python counter_dashboard.py # terminal 3
```
---
+30 -7
View File
@@ -2,7 +2,9 @@
"""
Standalone status webhook (stdlib only, HTTP + HTTPS).
Receives GET /?lokasi=IN|OUT&status=ON|OFF.
Receives:
GET /status -> {"status": "IN"|"OUT"|"OFF"}
GET /?lokasi=IN|OUT&status=ON|OFF -> success | fail
State stored in JSON as IN | OUT | OFF.
@@ -19,6 +21,7 @@ from __future__ import annotations
import json
import os
import signal
import ssl
import subprocess
import threading
@@ -144,7 +147,12 @@ class StatusHandler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
parsed = urlparse(self.path)
if parsed.path not in ("/", ""):
path = parsed.path.rstrip("/") or "/"
if path == "/status":
current = load_status() or "OFF"
self._send_json(200, {"status": current})
return
if path not in ("/", ""):
self._send_text(404, "Not Found")
return
@@ -184,6 +192,14 @@ class StatusHandler(BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(raw)
def _send_json(self, code: int, payload: dict) -> None:
raw = json.dumps(payload).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def make_http_server(port: int = HTTP_PORT) -> ThreadingHTTPServer:
return ThreadingHTTPServer(("0.0.0.0", port), StatusHandler)
@@ -256,13 +272,20 @@ def main() -> None:
if http_server is None and https_server is None:
raise SystemExit("status_webhook: neither HTTP nor HTTPS could bind")
stop = threading.Event()
def _handle_stop(_signum, _frame) -> None:
stop.set()
signal.signal(signal.SIGTERM, _handle_stop)
signal.signal(signal.SIGINT, _handle_stop)
try:
# serve_forever already running on threads; park main until interrupt.
while True:
threading.Event().wait(3600)
except KeyboardInterrupt:
print("\nstatus_webhook stopped")
# serve_forever already running on threads; park main until stop.
while not stop.is_set():
stop.wait(3600)
finally:
print("status_webhook stopped")
stop_servers(http_server, https_server)
+32
View File
@@ -0,0 +1,32 @@
[Unit]
Description=ZenAI KPC Status Webhook (HTTP + HTTPS)
Documentation=file:///opt/zenai-kpc-python/DEPLOY.md
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/opt/zenai-kpc-python
EnvironmentFile=/opt/zenai-kpc-python/.env
Environment=PYTHONNOUSERSITE=1
Environment=PATH=/opt/zenai-kpc-python/venv/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/opt/zenai-kpc-python/venv/bin/python status_webhook.py
TimeoutStopSec=15
KillSignal=SIGTERM
Restart=always
RestartSec=5
StartLimitInterval=60s
StartLimitBurst=3
NoNewPrivileges=true
ProtectHome=true
PrivateTmp=false
[Install]
WantedBy=multi-user.target