---
name: kami-conflict-detection
description: |
  Detect physical conflicts (fighting, shoving, scuffling) between 2+ people from RTSP camera
  streams or local video files. Event-driven mode: the script exits immediately upon detecting
  a conflict (exit code 10), outputting alert JSON to stdout. OpenClaw reads the alert, reports
  to the user in chat, then automatically restarts the script for continuous monitoring.
version: 2.0.0
author: kami-smarthome
tags:
  - smart-home
  - conflict-detection
  - fight-detection
  - violence-detection
  - yolo
  - rtsp
  - surveillance
  - security
  - edge-ai
  - event-driven
  - openclaw
triggers:
  - detect fighting
  - detect conflict
  - detect physical conflict
  - check for fighting
  - is anyone fighting
  - detect scuffle
  - detect shoving
  - monitor for fights
  - start conflict monitoring
  - begin violence detection
metadata:
  openclaw:
    requires:
      bins:
        - python3.10
      hardware:
        cpu: "4+ cores (x86_64 / ARM64)"
        memory: "8 GB+"
        storage: "10 GB+"
        gpu: "optional (speeds up ONNX inference)"
      network:
        - "RTSP camera access (LAN)"
        - "Internet (KamiClaw API)"
      devices:
        - "RTSP IP camera"
    emoji: "🥊"
---

# Kami Conflict Detection

Detect physical conflicts (fighting, shoving, scuffling) between 2+ people from RTSP camera streams or local video files. Uses an event-driven architecture where OpenClaw schedules the script in a loop for continuous real-time monitoring.

## Privacy Policy

For privacy policy details, see: <https://kamiclaw-skill.kamihome.com/privacy>

## How It Works

1. **YOLO pre-filter** — lightweight person detection counts people in the frame (must be ≥ 2).
2. **Multi-frame collection** — collects N frames with a configurable time gap.
3. **LLM conflict analysis** — frames are sent to the Kami detection API for violence/conflict judgment.
4. **Event-triggered exit** — on conflict detection, the script saves a video clip, prints alert JSON to stdout, and exits with code `10`. OpenClaw then restarts it.

## When to Use

- Monitor a camera feed for physical fights or scuffles
- Detect shoving, pushing, or violent behavior between people
- Run conflict detection on a local video file for testing
- Set up automated surveillance alerts for physical altercations

## Installation

```bash
bash setup.sh
```

No `sudo` required. The script auto-bootstraps **python3.10** in user space (via [uv](https://github.com/astral-sh/uv) when needed), creates `.venv/`, installs dependencies, and prepares `alerts/`. Idempotent — safe to re-run.

## Prerequisites

- Linux/macOS shell with `curl` (or `wget`) available
- `yolov8s-worldv2.onnx` model file in the skill directory
- RTSP camera online, OR a local video file for testing
- Kami API key (`--kami_api_key` or write to `config.json`). Register at <https://kamiclaw-skill.kamihome.com> for a free 200-credit quota.
- `setup.sh` has been run at least once

> Python 3.10 is **not** a manual prerequisite — `setup.sh` will install it locally without sudo if missing.

## Parameters

Confirm the following before running. The fields marked **(persisted in `config.json`)** can be saved to `config.json` next to the script so the user does not need to provide them every run — see [Configuration Persistence](#configuration-persistence) below.

| Parameter | Default | Description |
|-----------|---------|-------------|
| `--rtsp_url` | *(persisted in `config.json`)* | RTSP camera URL or local video file path |
| `--kami_api_key` | *(persisted in `config.json`)* | Kami API key |
| `--yolo_model` | `yolov8s-worldv2.onnx` | YOLO model file path |
| `--conf_threshold` | `0.25` | YOLO confidence threshold |
| `--min_persons` | `2` | Minimum person count to trigger LLM analysis |
| `--sample_interval` | `1.0` | YOLO pre-filter interval (seconds) |
| `--multi_frame_count` | `3` | Frames per LLM analysis |
| `--multi_frame_gap` | `0.5` | Gap between collected frames (seconds) |
| `--buffer_seconds` | `30` | Ring buffer duration for clip export |
| `--clip_before` | `5` | Seconds of video before the conflict |
| `--clip_after` | `5` | Seconds of video after the conflict |
| `--output_dir` | `alerts/` | Directory for saved video clips |
| `--run_time` | `0` | Max single-round run time; `0` = unlimited |
| `--fps` | `15` | Video stream frame rate |
| `--inbox_file` | `alerts/pending.jsonl` | Alarm inbox consumed by the heartbeat task |
| `--feishu_webhook` | *(persisted in `config.json`)* | Feishu custom bot webhook URL |
| `--feishu_secret` | *(optional, command-line only)* | Feishu signing secret (only if signing enabled) |
| `--discord_webhook` | *(persisted in `config.json`)* | Discord channel webhook URL |
| `--telegram_bot_token` | *(persisted in `config.json`)* | Telegram Bot token |
| `--telegram_chat_id` | *(persisted in `config.json`)* | Telegram target chat/group/channel ID |
| `--proxy` | *(optional, command-line only)* | HTTPS proxy for Discord/Telegram (not used for Feishu) |

**Only ask the user about a parameter if (a) it's still empty in `config.json` AND has no command-line value, OR (b) the user explicitly asks to adjust it. Do NOT pause the conversation for blanket parameter confirmation.**

## Configuration Persistence (`config.json`)

A `config.json` file lives next to the script with the following empty-by-default fields:

```json
{
  "rtsp_url": "",
  "kami_api_key": "",
  "feishu_webhook": "",
  "discord_webhook": "",
  "telegram_bot_token": "",
  "telegram_chat_id": ""
}
```

Resolution order at runtime: **command-line argument** → **`config.json`** → empty (skipped).

**Workflow OpenClaw MUST follow (single-turn, no extra confirmation):**

1. On first launch, read `config.json` and identify which fields are still empty.
2. **Ask the user only for the empty fields** (RTSP URL, Kami API key, and which push channels to enable + their credentials).
3. **Write the user's answers back into `config.json`** (preserve existing non-empty fields). Subsequent launches skip these prompts.
4. **Immediately in the SAME turn**, run `bash setup.sh` (idempotent) and launch the detector. Do NOT end the turn after step 3 — do NOT wait for the user to say "start" or "begin".
5. If all required fields are already non-empty on entry, skip steps 2–3 and go straight to step 4.

## Alarm Push Channels

Alarms can be pushed through the following channels — all optional, configure any combination:

| Channel | Required Parameters |
|---------|--------------------|
| **Feishu** (custom bot) | `--feishu_webhook` (and optional `--feishu_secret`) |
| **Discord** (channel webhook) | `--discord_webhook` |
| **Telegram** (Bot API) | `--telegram_bot_token` + `--telegram_chat_id` |

Beyond app push, every alarm is **always** delivered through two redundant local channels:

1. **stdout JSON + exit(10)** — OpenClaw reads stdout, reports in chat, restarts the script.
2. **Inbox file `alerts/pending.jsonl`** — appended on every alarm; consumed by the heartbeat task.

> Refer to the official docs of each platform for how to obtain webhook URLs / bot tokens / chat IDs.
> In mainland China, Discord and Telegram require a proxy (`--proxy` or `HTTPS_PROXY`).
> Push card labels are language-fixed: **Feishu → Chinese**, **Discord/Telegram → English**.

## Usage

```bash
# First time only
bash setup.sh

# Run with RTSP stream
.venv/bin/python conflict_detector_last.py \
  --rtsp_url rtsp://127.0.0.1/live/YOUR-STREAM-ID \
  --kami_api_key YOUR-API-KEY

# Run with local video file
.venv/bin/python conflict_detector_last.py \
  --rtsp_url /path/to/test_video.mp4 \
  --kami_api_key YOUR-API-KEY
```

## Output Format (stdout JSON)

When a conflict is detected (exit code 10):

```json
{
  "alert": "conflict_detected",
  "timestamp": "2025-01-15T14:30:22.123456",
  "description": "Two people are engaged in a physical altercation",
  "video_clip": "alerts/conflict_20250115_143022.mp4",
  "clip_duration": "10s",
  "message": "Warning: Physical conflict detected. ..."
}
```

## Exit Codes

| Code | Meaning | OpenClaw Action |
|------|---------|-----------------|
| `0` | Normal exit (timeout, video ended, no event) | Inform user; optionally restart |
| `10` | **Event detected** — alert JSON on stdout | Parse JSON, report to user, **immediately restart** |
| `1` | Runtime error | Report error; check `conflict_detector.log` |

## Strict Rules (MUST Follow)

- **RULE**: Alarms flow via (a) stdout+exit(10), (b) inbox file, (c) Feishu (optional), (d) Discord (optional), (e) Telegram (optional). Never rely on a single channel.
- **RULE**: Every heartbeat consumes `alerts/pending.jsonl`; non-empty → proactive chat summary; empty → `HEARTBEAT_OK`.
- **RULE**: Consumed alarms are MOVED to `alerts/consumed/`, not deleted.
- **RULE**: Before launch, read `config.json`; only ask the user for fields that are empty, and **write the answers back into `config.json`** so subsequent launches are non-interactive.
- **RULE (auto-launch)**: Once `rtsp_url` and `kami_api_key` are present in `config.json` (either pre-existing or just written), the agent MUST run `bash setup.sh` and launch the detector **in the same conversation turn**. Never end the turn at "config saved" — the user does NOT need to send a second message like "start it" or "begin detection".
- **RULE**: Warn the user if no push channel is configured (chat-window push still active).
- **RULE**: On exit code `10`, OpenClaw MUST restart the script immediately to continue monitoring.

## Troubleshooting

| Problem | Fix |
|---------|-----|
| Virtual environment not found | Run `bash setup.sh` |
| Model file missing | Place `yolov8s-worldv2.onnx` in the skill directory |
| RTSP connection failure | Verify camera is online; check `--rtsp_url` |
| LLM API failure | Check `KAMI_API_KEY`; verify network to the Kami API endpoint |
| No alerts generated | See `conflict_detector.log`; try lowering `--conf_threshold` |
| Script exits with code 1 | Check log; common causes: missing model, unreachable RTSP, missing API key |
