Kami Video Search

RTSP/RTMP camera stream recording with AI-powered video search. Start/stop background recording, check status, search video clips by natural language description, list recent events, and view logs. Triggers on: record, recording, camera, monitor, surveillance, stream, video search, clip search

Audits

Pass

Install

openclaw skills install kami-video-search

Kamivision Video Recorder & Search

Turn your cameras into a smart memory bank. No more scrubbing through hours of footage — just describe what you're looking for in plain language and instantly find it. Record 24/7, search by "someone in a red jacket walked past the door this morning," and jump straight to the clip. Setup takes 2 minutes. Sign up now and get 200 Credits for free — start searching your footage today!

Capabilities

  1. Start recording — launch background stream recording
  2. Stop recording — stop background recording
  3. Check status — view recording process status
  4. Search video — find video clips by natural language description (with optional time range)
  5. List recent events — show recent non-static video events
  6. View logs — display recording logs for troubleshooting

Trigger Words (Intent Routing)

Match user intent using these trigger words first, then fall back to semantic understanding:

IntentTrigger Words (EN)
Start recordingstart record, start recording, start camera, start monitor, start stream, launch recording
Stop recordingstop record, stop recording, stop camera, stop monitor, stop stream, kill recording
Check statusrecording status, camera status, monitor status, is recording, check status
Search videosearch video, find video, find clip, look for, search footage, video search
List recentrecent events, recent clips, list events, what happened, show recent
View logsshow logs, view logs, recording logs, check logs

If no trigger word matches, use semantic understanding to route to the closest intent.

First-Time Setup Flow

IMPORTANT: On first use, you MUST complete the setup flow before any other operation.

DO NOT use system Python directly. This skill requires its own isolated virtual environment (.venv) in the skill directory.

Step 0: Setup Flow (Follow in Order)

Follow these steps in exact order. Do not skip ahead.


Step 0.1: Check System Python (MUST be >= 3.8)

First, check if Python 3.8+ is installed on the system:

python3 --version

If Python 3.8+ is NOT found (command fails or version is below 3.8):

Tell the user:

🐍 Python 3.8 or higher is required but was not found on your system.

Install Python:

After installing, run python3 --version again to confirm.

DO NOT proceed until Python 3.8+ is confirmed.


Step 0.2: Check/Create Virtual Environment in Skill Directory

Once Python 3.8+ is confirmed, check if .venv exists in the skill directory:

ls -la {baseDir}/.venv/bin/python3

If .venv does NOT exist:

Tell the user:

📦 This skill needs its own isolated Python environment. I'll guide you to create it.

Step 1: Create the virtual environment

cd {baseDir}
python3 -m venv .venv

Step 2: Verify it was created

ls -la .venv/bin/

You should see: python, python3, pip, pip3, activate, etc.

Step 3: Activate and install dependencies

source .venv/bin/activate
pip install -r requirements.txt

Step 4: Verify installation

.venv/bin/python -c "import numpy; import requests; import cv2; print('All dependencies OK')"

If this prints "All dependencies OK", setup is complete!

DO NOT proceed until the user confirms .venv is created and dependencies are installed.

If .venv already exists:

  1. Verify the Python version:
{baseDir}/.venv/bin/python3 --version
  1. Verify dependencies are installed:
{baseDir}/.venv/bin/python -c "import numpy; import requests; import cv2; print('All dependencies OK')"
  • If imports succeed, proceed to configuration.
  • If imports fail, tell the user:

⚠️ Dependencies are missing or broken. Let's reinstall:

cd {baseDir}
source .venv/bin/activate
pip install -r requirements.txt

Pre-Recording Configuration

⚠️ MANDATORY: Before EVERY recording start, you MUST confirm all Required Parameters with the user.

DO NOT start recording without explicit user confirmation of these parameters. This is a mandatory step every single time, even if the values haven't changed since the last recording.

Mandatory Confirmation Flow

⚠️ CRITICAL: Display FULL detailed explanations for EVERY parameter. Do NOT use shortened summaries.

  1. Read current config from {baseDir}/stream_config.json
  2. Display ALL Required Parameters to the user with COMPLETE detailed explanations including:
    • Current value
    • What it does: Full explanation of the parameter's purpose and how it's used in the code
    • How to get it: Step-by-step guidance on obtaining or choosing the right value
    • Format: Expected format with examples
    • Range: Valid values and constraints
    • Default: If applicable
    • Impact of different values: Detailed breakdown of how different values affect behavior, costs, storage, API usage, etc. Include scenarios and recommendations.
    • Error behavior: What happens if configured incorrectly
  3. Ask for explicit confirmation:
    • "Please confirm if the above configuration is correct. See the detailed explanations above for each parameter. Let me know if you need to modify any parameters."
  4. Wait for user response:
    • If user says "yes" → proceed to start recording
    • If user provides new values → update config, then confirm again
  5. Only after confirmation → execute the start recording command

When displaying parameters, use this format for each:

Language Rule: Match the user's language. Use English labels and explanations.

Parameter display format:

📹 STREAM_URL = <current_value>
   What it does: <full explanation>
   How to get it: <step-by-step guidance>
   Format: <format with examples>
   Range: <valid values>
   Default: <default value if applicable>
   Impact of different values: <detailed breakdown with scenarios>
   Error behavior: <consequences>

IMPORTANT: Use the complete parameter explanations from the "Required Parameters" section below. Do NOT summarize or shorten them. The user needs full context to make informed decisions.

Required Parameters (MUST confirm every time with full explanations)

Language Rule: Display parameter explanations in the same language the user uses. Use English labels (What it does, Format, Range, Default, Error behavior) and English explanations.


English Version (for English-speaking users)

  1. STREAM_URL (Camera stream address)

    • What it does: The RTSP/RTMP stream URL of your camera. The code passes this directly to cv2.VideoCapture(STREAM_URL) to open the video stream. It supports RTSP, RTMP, and HTTP protocols.
    • How to get it:
      • Check your camera's admin web interface (usually at http://camera-ip/)
      • Look for "RTSP URL" or "Stream URL" in camera settings
      • Common formats: Hikvision rtsp://user:pass@ip:554/Streaming/Channels/1, Dahua rtsp://user:pass@ip:554/cam/realmonitor?channel=1&subtype=0
      • Contact your camera manufacturer's support if unsure
    • Format: rtsp://username:password@IP:port/path (e.g., rtsp://admin:password@192.168.1.100:554/stream1)
    • Range: Any valid stream URL. Validate: MUST start with rtsp://. Reject any other format and ask user to correct it.
    • Impact of different values:
      • Correct URL with valid credentials → Smooth recording, stable frames
      • Wrong IP/port → Connection timeout, recording fails after reconnection attempts
      • Wrong credentials → Authentication failure, no video stream
      • Unreachable camera (offline/network issue) → Same as wrong URL, recording stops
    • Error behavior: If the URL is wrong or unreachable, cv2.VideoCapture will fail to open. After 100 consecutive frame read failures, the code treats it as a stream disconnection and attempts reconnection (up to MAX_RECONNECT times, default 3). If all reconnections fail, recording stops entirely.
  2. KAMI_API_KEY (Kamivision API key)

    • What it does: Authentication key sent in the X-API-Key HTTP header when calling the Kamivision https://kamiclaw-skill-api.kamihome.com/v1/detect API for video description generation and text embedding. Without it, all API calls will return authentication errors, and no descriptions or embeddings will be generated — video search will not work.
    • How to get it:
      • Visit https://kamiclaw-skill.kamihome.com, first registration offers 200 Credits of free credit limit.
      • Sign up / log in to your Kamivision account
      • Navigate to API Keys section
      • Create a new key or copy existing one (starts with sk_live_)
      • Keep this key secret — do not share publicly
    • Format: String starting with sk_live_ (e.g., sk_live_xxxxxxxxxxxxxxxx)
    • Impact of different values:
      • Valid key → API calls succeed, video descriptions generated, search works
      • Invalid/expired key → API returns 401/403, no descriptions generated, recording continues but search won't work
      • Empty key → Same as invalid, all API calls fail immediately
      • Rate limit exceeded → API returns 429, code retries per KAMI_API_RETRY, may lose some descriptions
    • Error behavior: If empty or invalid, the API returns a non-200 code. The code retries up to KAMI_API_RETRY times (default 3). After all retries fail, a RuntimeError is raised, the worker thread sends SIGTERM to the process, and recording stops.
  3. DEVICE_ID (Camera identifier)

    • What it does: A label for this camera. Used in three places: (1) video file naming pattern {DEVICE_ID}_{date}_{time}_{index}.mp4, (2) storage directory structure {DATA_DIR}/{DEVICE_ID}/{date}/{hour}/, (3) the device_id field in the SQLite index database.
    • How to get it:
      • Choose a meaningful name for this camera location (e.g., front-door, living-room, warehouse-1)
      • If you have multiple cameras, use unique IDs for each (e.g., CAM-001, CAM-002)
      • Use existing camera name from your NVR/DVR system for consistency
    • Default: CAM-001
    • Range: Any string that is valid as a file/directory name. Avoid special characters like /, \, :, *, ?, ", <, >, |.
    • Impact of different values:
      • Descriptive name (e.g., front-door) → Easy to identify videos, organized file structure
      • Generic name (e.g., CAM-001) → Works fine, but less identifiable when managing multiple cameras
      • Changing DEVICE_ID later → Creates new subdirectory; old videos remain under old DEVICE_ID folder (manual migration needed if you want to consolidate)
      • Special characters in name → File/directory creation fails, recording cannot start
    • Error behavior: If it contains invalid filesystem characters, mkdir or file creation will fail with an OSError, and recording cannot start.
  4. SKIP_STATIC (Skip static frames)

    • What it does: Controls whether the AI description API is called for static (no-motion) video segments. Regardless of this setting, all video segments are always saved to disk. When true, each segment is analyzed by is_static_video() — it samples frame pairs, converts to grayscale, and computes mean absolute pixel difference. If below STATIC_THRESHOLD, the segment is marked is_static=True in the database, and the API call is skipped (no description or embedding generated). This means static segments are still recorded on disk but are invisible to search and list queries — they have no description or embedding, so search cannot match them and list_recent explicitly filters them out (not rec.get("is_static", False)).
    • Default: true (recommended)
    • Range: true or false (boolean)
    • Impact of different values:
      • true (recommended for most cases):
        • ✅ Saves API costs — only sends clips with actual motion to Kamivision
        • ✅ Faster search — fewer indexed entries, queries are quicker
        • ⚠️ Video files are still saved to disk — no disk space savings on video storage
        • ⚠️ Static segments are unsearchable — they exist on disk but cannot be found via search or list commands
        • ⚠️ May miss subtle events — very slight movements might be classified as static and become unsearchable
        • Best for: 24/7 recording, low-traffic areas, cost-conscious setups
      • false (describe everything):
        • ✅ Every segment gets a description and embedding — all clips are searchable
        • ✅ Complete searchable timeline — no gaps in search results
        • ❌ Higher API costs — every segment calls Kamivision API (including boring static footage)
        • ❌ Larger database — more index entries, slightly slower search
        • ⚠️ Disk usage is the same — video files are saved regardless
        • Best for: High-security areas, short-term recording, unlimited API budget
    • Error behavior: If set to a non-boolean value, Python's truthy evaluation applies — any non-empty string or non-zero number is treated as true. Setting to false means every segment will be sent to the API, which increases API usage and database size (but not video disk usage).
  5. STATIC_THRESHOLD (Motion sensitivity)

    • What it does: The threshold used by is_static_video() to decide if a video segment is "static". The function computes np.mean(np.abs(frame1 - frame2)) for sampled grayscale frame pairs (pixel values 0–255). If the average of all pair differences is below this threshold, the segment is considered static.
    • Default: 5.0
    • Range: 0.0255.0 (theoretical). Practical range: 1.020.0.
    • Only effective when: SKIP_STATIC is true
    • Impact of different values:
      • Very low (0.0 – 1.0):
        • Almost nothing is skipped — even completely still scenes pass through
        • API calls: Maximum (nearly 100% of segments sent)
        • Use case: Maximum sensitivity, don't miss anything (e.g., monitoring delicate equipment)
        • Downside: High API costs, defeats the purpose of SKIP_STATIC
      • Low (1.0 – 5.0):
        • Catches subtle changes — lighting shifts, leaves swaying, small animals
        • API calls: High (70-90% of segments sent)
        • Use case: Outdoor scenes with wind, areas with frequent minor movements
        • Downside: May still send many "unimportant" clips
      • Medium (5.0 – 15.0) — RECOMMENDED:
        • Normal motion detection — people walking, vehicles passing, doors opening
        • API calls: Moderate (20-50% of segments sent, depends on location)
        • Use case: Indoor rooms, entrances, hallways, parking lots
        • Best balance: Catches meaningful events while skipping empty periods
      • High (15.0 – 50.0):
        • Only significant motion — multiple people, fast vehicles, major activity
        • API calls: Low (5-20% of segments sent)
        • Use case: Very busy areas where you only care about major events
        • Downside: May miss single-person entries or slow movements
      • Very high (> 50.0):
        • Only extreme motion — crowds, chaos, rapid movement
        • API calls: Minimal (< 5% of segments sent)
        • Use case: Almost never recommended — you'll miss most events
        • Downside: Essentially records only when there's intense activity
    • Tuning tips:
      • Start with 5.0, monitor for a day, check what got skipped
      • If too many false positives (static clips sent): increase by 2-3
      • If missing important events: decrease by 2-3
      • Different locations need different values — test and adjust
    • Error behavior: No validation in code. A negative value means nothing is ever static (all segments processed). An extremely high value (e.g., 200) means almost everything is skipped. Non-numeric values will cause a TypeError crash in np.mean() comparison.
  6. DATA_DIR (Storage directory)

    • What it does: Root directory for all recorded data. The code creates a hierarchical structure: {DATA_DIR}/{DEVICE_ID}/{YYYYMMDD}/{HH}/ for video files, and {DATA_DIR}/{DEVICE_ID}/index.db for the SQLite index. Directories are created automatically via Path.mkdir(parents=True, exist_ok=True).
    • How to get it:
      • Choose a directory with sufficient disk space
      • For Linux/Mac: /home/youruser/video_data or /mnt/storage/videos
      • For Windows: D:\video_data or C:\Users\YourName\video_data
      • Use external drive or NAS for long-term storage
      • Ensure the user running the recorder has write permissions
    • Default: ./video_data (relative to working directory)
    • Range: Any valid filesystem path (absolute or relative). Ensure the path is writable and has sufficient disk space. Each 10-second clip at 640×360 is roughly 200KB–1MB.
    • Impact of different values:
      • Local disk (fast SSD):
        • ✅ Fast write speeds, reliable recording
        • ✅ Quick search and playback
        • ⚠️ Limited capacity (check free space: df -h)
        • Estimate: 1 hour ≈ 72-360 clips ≈ 15-360 MB (depends on motion)
      • External HDD / NAS:
        • ✅ Large capacity for long-term storage
        • ⚠️ Slower write speeds — ensure stable network for NAS
        • ⚠️ Network issues can interrupt recording
        • Best for: Archive storage, multi-camera setups
      • Small partition (< 10 GB free):
        • ⚠️ Will fill up quickly — monitor disk space
        • With RETENTION_DAYS=3: Should auto-clean, but verify regularly
        • Risk: If disk fills, recording produces corrupted files
      • Changing DATA_DIR later:
        • Old videos stay in old directory (manual migration needed)
        • New recordings go to new directory
        • Index database is per DEVICE_ID, so search only sees new videos
    • Disk space estimation:
      • Per 10-second clip: ~200KB (static) to ~1MB (high motion)
      • Per hour: ~72 clips × average size
      • Example: 50% motion, 500KB avg → 72 × 500KB ≈ 36 MB/hour
      • Per day: ~864 MB (continuous recording, moderate motion)
      • With SKIP_STATIC=true: Can reduce by 50-80% in low-traffic areas
    • Error behavior: If the path is not writable (permission denied), Path.mkdir() or cv2.VideoWriter() raises OSError/PermissionError and recording fails. If disk space runs out, cv2.VideoWriter.write() silently produces corrupted files (0 bytes), which are then skipped by the processing queue.
  7. RETENTION_DAYS (Data retention period)

    • What it does: Controls automatic cleanup of old recordings. A background thread runs index.purge_expired(RETENTION_DAYS) every hour. It queries the SQLite database for records where created_at < (now - RETENTION_DAYS), deletes the corresponding video files from disk, removes the database records, and cleans up empty directories. After purging, it runs VACUUM to reclaim database file space.
    • Default: 3 (days)
    • Range: Integer >= 0. Set to 0 to disable auto-cleanup (keep forever). No upper limit, but very large values effectively disable cleanup.
    • Impact of different values:
      • 0 (disable cleanup):
        • ✅ Keep everything forever — complete archive
        • ❌ Disk space grows indefinitely — will eventually fill up
        • ❌ Database grows larger — search may slow down over time
        • Use case: Short-term testing, external backup system handles cleanup
        • ⚠️ WARNING: Monitor disk space manually!
      • 1-2 days:
        • ✅ Minimal disk usage — aggressive cleanup
        • ❌ May lose important footage if you don't check daily
        • ❌ Not enough time to review and export important clips
        • Use case: High-motion areas, limited disk space, daily review habit
      • 3-7 days — RECOMMENDED:
        • ✅ Balanced approach — enough time to review and export
        • ✅ Reasonable disk usage for most setups
        • ✅ Weekly review cycle works well
        • Use case: Most home/office security setups
        • Estimate: 7 days × ~1 GB/day ≈ 7 GB per camera (moderate motion, SKIP_STATIC=true)
      • 8-30 days:
        • ✅ Extended review window — don't miss old events
        • ❌ Higher disk usage — ensure adequate capacity
        • ❌ Larger database — slightly slower search
        • Use case: Compliance requirements, incident investigation, low-motion areas
      • > 30 days:
        • ✅ Long-term archive on local disk
        • ❌ Significant disk space required
        • ❌ Consider external storage or cloud backup instead
        • Use case: Legal/compliance requirements, very low motion areas
    • Disk space planning examples (per camera, SKIP_STATIC=true, moderate motion):
      • RETENTION_DAYS=3: ~3 GB
      • RETENTION_DAYS=7: ~7 GB
      • RETENTION_DAYS=14: ~14 GB
      • RETENTION_DAYS=30: ~30 GB
      • Multiply by number of cameras for total estimate
    • Manual override: You can manually delete old videos anytime by removing files from {DATA_DIR}/{DEVICE_ID}/ — the database will auto-cleanup orphaned entries on next scan
    • Error behavior: Negative values behave the same as 0 (no cleanup) because the cutoff time would be in the future. Non-integer values are used directly in timedelta(days=...) — floats work (e.g., 0.5 = 12 hours), but strings cause a TypeError crash.
  8. SUMMARY_UPLOAD_MODE (API upload mode)

    • What it does: Controls what data is sent to the Kamivision API for AI analysis. In image mode (default), only extracted frame snapshots are uploaded. In video mode, the entire video file is base64-encoded and uploaded. This directly affects privacy — video mode sends complete footage to the external API.
    • Default: image
    • Range: "image" or "video" (string)
    • Impact of different values:
      • image (recommended):
        • ✅ Only frame snapshots leave your machine — minimal data exposure
        • ✅ Smaller upload payload — faster API calls, lower bandwidth
        • ⚠️ May produce less detailed descriptions than video mode
      • video:
        • ✅ More detailed AI descriptions — full video context available
        • ❌ Entire video file is sent to the Kamivision API — significant privacy implications
        • ❌ Larger upload payload — slower API calls, higher bandwidth usage
        • ⚠️ Only use if you understand and accept that full footage leaves your machine
    • Error behavior: If set to any value other than "video", the code falls back to image mode. No crash risk.

Configuration Update

IMPORTANT: Only update config and start recording AFTER explicit user confirmation.

After the user confirms the parameters (or provides updated values):

  1. Read the current config from {baseDir}/stream_config.json
  2. Update only the confirmed/changed fields
  3. Write the updated config back
  4. Then proceed to execute the start recording command

Never skip the confirmation step or assume previous values are still valid.

Command Execution

All commands MUST use the virtual environment Python interpreter at {baseDir}/.venv/bin/python.

All commands use the script at {baseDir}/stream_recoder2.py with config at {baseDir}/stream_config.json.

Start Recording

{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --start-daemon --log-file {baseDir}/stream_recorder.log

After running, parse the JSON output:

  • If status is "started": Tell user recording has started, show the PID
  • If status is "already_running": Tell user recording is already running, show the PID
  • On error: Show the error message and ask user to check their STREAM_URL and network

Stop Recording

{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --stop-daemon

Parse the JSON output:

  • If status is "stopped": Confirm recording has stopped
  • If status is "not_running": Tell user there's no active recording to stop

Check Status

{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --status

Parse the JSON output and report the status to the user in a friendly way.

Search Video

The user may provide a search query in natural language, optionally with a time range.

Time range parsing:

  • Natural language: "today morning", "yesterday afternoon", "this morning 8 to 12" → convert to YYYY-MM-DD_HH:MM:SS format
  • Fixed format: accept YYYY-MM-DD_HH:MM:SS or YYYY-MM-DD HH:MM:SS directly
  • If no time range specified, search all recorded footage
{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --search "QUERY_TEXT" --json

With time range:

{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --search "QUERY_TEXT" --time-start "YYYY-MM-DD_HH:MM:SS" --time-end "YYYY-MM-DD_HH:MM:SS" --json

Parse the JSON output and present results to the user:

  • Show the number of matches
  • For each result: time, description, score, and full video file path
  • If no results: tell the user no matching clips were found, suggest broadening the query or time range

List Recent Events

{baseDir}/.venv/bin/python {baseDir}/stream_recoder2.py --config {baseDir}/stream_config.json --list HOURS --json

Default HOURS to 24 if user doesn't specify. Parse and present results showing time, description, and video path.

View Logs

tail -100 {baseDir}/stream_recorder.log

Show the last 100 lines of the log file. If the user asks for more, increase the line count. If the log file doesn't exist, tell the user recording hasn't been started yet.

Error Handling

  • STREAM_URL connection failure: Tell the user to check the camera address, network connectivity, and credentials. Do NOT retry automatically.
  • Invalid API Key: Tell the user "Your Kamivision API key appears to be invalid. Please check it at https://kamiclaw-skill.kamihome.com". Do NOT retry.
  • Python not found: Guide installation per Step 0 above.
  • Virtual environment missing: Create .venv per Step 0, then install dependencies.
  • Dependency missing: Re-run {baseDir}/.venv/bin/pip install -r {baseDir}/requirements.txt.
  • Permission denied: Suggest running with appropriate permissions or changing DATA_DIR to a writable location.

Security & Privacy

  • Credentials: The stream_config.json file contains the camera stream URL (which may include credentials) and the Kamivision API key. Use a dedicated camera account and API key with the least privileges. Never share this file. Rotate credentials if you uninstall or stop using the skill.
  • Data sent to API: By default (SUMMARY_UPLOAD_MODE=image), only extracted frame snapshots are sent to the Kamivision API. If SUMMARY_UPLOAD_MODE=video, the entire video file is uploaded. Always verify this setting before starting recording.
  • Local data: The SQLite index database stores descriptions and embeddings of camera clips, creating a searchable record of recorded events. Store the data directory in a protected location.
  • Background recording: The daemon continues recording until explicitly stopped. Periodically check status, logs, and disk usage.
  • Dependencies: Third-party packages are installed from PyPI with version ranges. For sensitive deployments, consider pinning exact versions. Always install in the isolated .venv.

Language

Respond in the same language the user uses. If the user writes in Chinese, respond in Chinese. If in English, respond in English.

For parameter explanations during recording setup:

  • Use English labels (What it does, Format, Range, Default, Error behavior) and English explanations.
  • Do not mix languages — if the user asks in English, show all explanations in English; if in Chinese, show all in Chinese.