Skip to content

arunrajiah/cctvql

cctvQL

Ask your CCTV system anything in plain English.

License: MIT Python CI codecov PyPI Docker PRs Welcome CodeQL Sponsor

cctvQL is an open-source conversational query layer for CCTV and surveillance systems. It wraps any camera system — Frigate, ONVIF, Hikvision, Dahua and more — with a natural language interface powered by local or cloud LLMs.

Quick Start · Documentation · Adapters · Contributing · Sponsor


What It Does

you      > Was there anyone near the front door last night?

cctvQL   > Yes — 3 person detections on "Front Door" between 22:14 and 23:47.
           • 22:14 — person (96%) in zone: driveway
           • 23:02 — person (88%)
           • 23:47 — person (91%) in zone: porch

you      > Show me the clip from 23:47

cctvQL   > Clip from Front Door (23:47–23:49, 112s):
           http://192.168.1.100:5000/api/events/abc123/clip.mp4

No dashboards. No complex queries. Just ask.


Features

  • Natural language queries — ask about events, cameras, clips, and system health in plain English
  • Multi-turn conversations — context-aware follow-up questions backed by SQLite session persistence
  • Vendor-agnostic — adapter pattern supports any CCTV system; ships with Frigate, ONVIF, Hikvision, Dahua
  • Pluggable LLM backends — Ollama (local/private), OpenAI, Anthropic, or any OpenAI-compatible API
  • REST API — integrate with Home Assistant, Grafana, custom dashboards, or mobile apps
  • PTZ control — pan, tilt, zoom and preset recall via REST API for supported cameras
  • Event export — download event history as CSV or JSON from /events/export
  • Alert rules — create rules to notify when specific labels appear on specific cameras in time windows
  • Multi-channel notifications — Telegram, Slack, ntfy, email, and webhook; all fire concurrently
  • WebSocket streaming — real-time event push to any connected client via ws://host/ws/events
  • Prometheus metrics/metrics endpoint for Grafana, alerting, and observability
  • Camera health monitoring — background poller tracks per-camera online/offline status
  • Optional API key auth — protect your endpoint with CCTVQL_API_KEY env var
  • Multi-tenant support — JWT auth, per-user camera permissions, admin user management (CCTVQL_MULTI_TENANT=1)
  • Anomaly detection — statistical spike/silence detection per camera; ask "anything unusual today?"
  • Demo adapter — try cctvQL without any hardware; realistic mock data built-in
  • Interactive CLI — terminal-based conversational REPL
  • Real-time events — MQTT subscription for live alerts (Frigate)
  • Home Assistant integration — native custom integration with sensors, binary sensors (per-camera motion), PTZ and query services; installable via HACS
  • Docker-ready — running in under 5 minutes with persistent SQLite storage

Screenshots

Web UI — landing cctvQL landing screen showing camera sidebar and example query chips

Camera list query Response showing all 4 cameras with status and zones

Event history Recent motion events on the Front Door camera with timestamps and confidence scores

Alert rule creation Natural language alert setup — person detected after 10pm

Multi-turn conversation with vision AI Multi-turn conversation including a GPT-4o Vision analysis of an event snapshot


Quick Start

Try it now — no hardware needed

pip install cctvql
cctvql chat --adapter demo --llm ollama

The demo adapter ships with 4 cameras, 20 realistic events, and 5 clips — no Frigate or ONVIF device required. Use it to explore the query interface, build integrations, or write tests.

Docker (recommended — 5 minutes)

# 1. Clone and configure
git clone https://github.com/arunrajiah/cctvql.git
cd cctvql
cp config/example.yaml config/config.yaml

# 2. Edit config/config.yaml with your Frigate URL and LLM settings
nano config/config.yaml

# 3. Start
docker compose up -d

# 4. Try it
curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{"query": "Show me all cameras"}'

API docs: http://localhost:8000/docs

pip

pip install cctvql

# Interactive chat
cctvql chat --config config/config.yaml

# REST API server
cctvql serve --config config/config.yaml --port 8000

From source

git clone https://github.com/arunrajiah/cctvql.git
cd cctvql
pip install -e ".[dev,mqtt,onvif]"
cctvql chat

Documentation

Topic Link
Configuration reference docs/configuration.md
REST API reference docs/api.md
Notifications setup docs/notifications.md
Session & event persistence docs/persistence.md
Writing an adapter docs/adapters.md
LLM backend setup docs/llm-backends.md
Home Assistant integration docs/home-assistant.md
Docker deployment docs/docker.md
Frigate + cctvQL sidecar deploy/frigate-sidecar/
Troubleshooting docs/troubleshooting.md

Supported Systems

System Type Adapter Status
Frigate NVR NVR frigate ✅ Full support (REST + MQTT) — sidecar template
Any ONVIF camera/NVR Camera/NVR onvif ✅ Full support
Demo / Mock Built-in demo ✅ No hardware needed — try cctvQL now
Hikvision NVR/Camera hikvision ✅ Full support (ISAPI)
Dahua NVR/Camera dahua ✅ Full support (CGI)
Synology Surveillance Station NVR synology ✅ Full support (Web API)
Milestone XProtect Enterprise NVR milestone ✅ Full support (REST API)
Scrypted Smart home NVR scrypted ✅ Full support (Bearer token)
Your system Any Write an adapter!

Writing an adapter takes ~100 lines. See the adapter guide.


Supported LLM Backends

Backend Privacy Cost Quality
Ollama (llama3, mistral, phi3…) 🔒 100% local Free ⭐⭐⭐⭐
OpenAI (gpt-4o-mini, gpt-4o) ☁️ Cloud Paid ⭐⭐⭐⭐⭐
Anthropic (claude-haiku, claude-sonnet) ☁️ Cloud Paid ⭐⭐⭐⭐⭐
LM Studio 🔒 Local Free ⭐⭐⭐⭐
Any OpenAI-compatible API Varies Varies Varies

Recommended for privacy: Ollama with llama3 or mistral. No data leaves your network.


Configuration

# config/config.yaml
llm:
  active: ollama
  backends:
    ollama:
      provider: ollama
      host: http://localhost:11434
      model: llama3

adapters:
  active: frigate
  systems:
    frigate:
      type: frigate
      host: http://192.168.1.100:5000
      mqtt_host: 192.168.1.100      # optional, for real-time events

# Optional: alert notifications (any combination of channels)
notifications:
  telegram:
    bot_token: "123456:ABC-DEF..."
    chat_id: "-1001234567890"
  slack:
    webhook_url: "https://hooks.slack.com/services/..."
  ntfy:
    topic: my-cctvql-alerts

Database path (conversation history + event log):

export CCTVQL_DB_PATH=/data/cctvql.db

See docs/configuration.md for the full reference.
Notification channels: docs/notifications.md.
Session persistence: docs/persistence.md.


REST API

# Natural language query (multi-turn, session history persisted to SQLite)
POST /query
{"query": "Did anyone come to the front door after midnight?", "session_id": "my-session"}

# List cameras
GET /cameras

# Get events with filters
GET /events?camera=driveway&label=person&limit=10

# Export events as CSV or JSON
GET /events/export
GET /events/export?fmt=json&camera=Front+Door&label=person

# PTZ control (pan/tilt/zoom)
POST /cameras/{camera_id}/ptz
{"action": "left", "speed": 50}

# PTZ presets
GET /cameras/{camera_id}/ptz/presets

# System health
GET /health

# Per-camera health status
GET /health/cameras

# Alert rules (CRUD)
GET    /alerts
POST   /alerts
GET    /alerts/{id}
PATCH  /alerts/{id}
DELETE /alerts/{id}

# Anomaly detection (statistical spike/silence analysis)
GET /anomalies
GET /anomalies?hours=6&camera=Front+Door&threshold=1.5

# Prometheus metrics (for Grafana / alerting)
GET /metrics

# Clear conversation session (also removes from database)
DELETE /sessions/{session_id}

Real-time event streaming via WebSocket:

ws://localhost:8000/ws/events

Optional API key auth — set CCTVQL_API_KEY env var to require X-API-Key header on all requests.

Interactive Swagger docs available at http://localhost:8000/docs.

See docs/api.md for full endpoint documentation.


Architecture

┌─────────────────────────────────────────────────────────┐
│                    User Interface                        │
│         CLI Chat  │  REST API  │  Home Assistant         │
└─────────────────────────┬───────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────┐
│                    NLP Engine                            │
│   Natural Language → QueryContext (intent + params)      │
└─────────────────────────┬───────────────────────────────┘
                          │
           ┌──────────────▼──────────────┐
           │        LLM Registry         │
           │  Ollama │ OpenAI │ Anthropic │
           └──────────────┬──────────────┘
                          │
┌─────────────────────────▼───────────────────────────────┐
│                   Query Router                           │
│   Routes intent to adapter → formats human response     │
└─────────────────────────┬───────────────────────────────┘
                          │
           ┌──────────────▼──────────────┐
           │      Adapter Registry       │
           │  Frigate │ ONVIF │ Your NVR │
           └──────────────┬──────────────┘
                          │
┌─────────────────────────▼───────────────────────────────┐
│                  Your CCTV System                        │
│         NVR / IP Cameras / Recording Storage             │
└─────────────────────────────────────────────────────────┘

Project Structure

cctvql/
├── cctvql/
│   ├── core/
│   │   ├── schema.py          # Vendor-agnostic data models
│   │   ├── nlp_engine.py      # Natural language → QueryContext
│   │   └── query_router.py    # QueryContext → adapter → response
│   ├── adapters/
│   │   ├── base.py            # BaseAdapter interface (implement to add a system)
│   │   ├── frigate.py         # Frigate NVR (REST + MQTT)
│   │   └── onvif.py           # Generic ONVIF adapter
│   ├── llm/
│   │   ├── base.py            # BaseLLM interface + LLMRegistry
│   │   ├── ollama_backend.py  # Local LLM via Ollama
│   │   ├── openai_backend.py  # OpenAI / compatible APIs
│   │   └── anthropic_backend.py
│   ├── interfaces/
│   │   ├── cli.py             # Interactive terminal chat
│   │   └── rest_api.py        # FastAPI REST server
│   ├── _bootstrap.py          # Config loader and wiring
│   └── __main__.py            # Entry point (cctvql chat / serve)
├── config/
│   └── example.yaml           # Annotated config reference
├── docs/                      # Full documentation
├── tests/
├── Dockerfile
├── docker-compose.yml
└── pyproject.toml

Contributing

Contributions are what make cctvQL useful for everyone. The single highest-impact contribution is writing an adapter for a CCTV system you already have.

git clone https://github.com/arunrajiah/cctvql.git
cd cctvql
pip install -e ".[dev,mqtt,onvif]"
pytest tests/   # all tests should pass

See CONTRIBUTING.md for the full guide.

# Common developer commands
make dev          # install with all extras + pre-commit hooks
make test         # run test suite
make coverage     # tests + coverage report
make lint         # ruff linter
make type-check   # mypy
make demo         # interactive demo (no real hardware needed)

Most wanted contributions:

  • Hikvision adapter
  • Dahua adapter
  • Synology Surveillance Station adapter
  • Vision-based event description (send snapshot to LLM)
  • Home Assistant custom integration

FAQ

Does my camera data leave my network? Only if you use a cloud LLM backend (OpenAI, Anthropic). With Ollama, everything — including the AI processing — stays on your local machine.

Which Frigate version is supported? Frigate 0.12+ is tested. Most features work with 0.9+.

Can I use this with any ONVIF camera? Yes. ONVIF Profile S is supported for live streaming and snapshots. Profile G adds recording/clip support.

Can I query multiple camera systems at once? Yes — use "multi": true in your query request to fan out across all registered adapters simultaneously.

Is there a Home Assistant integration? A native Home Assistant custom integration is planned. For now, use the REST API endpoint from HA automations.


Roadmap

  • Vision analysis — pass event snapshots to multimodal LLMs for rich descriptions
  • Hikvision adapter
  • Dahua adapter
  • Web UI (lightweight chat interface)
  • Multi-system queries (query across multiple NVRs simultaneously)
  • Alert rules via natural language ("notify me when a person enters Zone A after 10pm")
  • Voice interface (Whisper STT + TTS output)
  • Multi-channel notifications — Telegram, Slack, ntfy, email, webhook
  • PTZ control via REST API (pan, tilt, zoom, presets)
  • Session persistence — conversation history survives server restarts (SQLite)
  • Event log — every detection written to SQLite; exportable as CSV/JSON
  • Camera health monitoring — per-camera online/offline status with background polling
  • Prometheus metrics — cameras online/offline, alert rule count
  • Home Assistant custom integration — sensors, binary sensors, services, HACS-ready
  • ONVIF discovery — auto-detect cameras on the local network (cctvql discover CLI + GET /discover/onvif)
  • Event timeline UI — visual heatmap timeline at /timeline with camera rows, time buckets, tooltips, auto-refresh
  • Face recognition — identify known individuals across camera feeds
  • Anomaly detection — statistical spike/silence detection per camera with z-score baseline (GET /anomalies)
  • Multi-tenant support — JWT auth, per-user camera permissions, admin user management (CCTVQL_MULTI_TENANT=1)
  • Mobile app (React Native)

Sponsor

cctvQL is free and open-source. If it saves you time or powers something you care about, consider sponsoring — it helps keep the project maintained and moving forward.

Sponsor arunrajiah


License

MIT © 2026 arunrajiah

See LICENSE for the full text.


If cctvQL is useful to you, please ⭐ the repo — it helps others find it!

About

Conversational query layer for CCTV and surveillance systems — ask questions in plain English, get answers from Frigate, ONVIF, and more.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors