Conversational query layer for CCTV systems — ask your cameras anything in plain English
Project description
cctvQL
Ask your CCTV system anything in plain English.
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.
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 —
/metricsendpoint 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_KEYenv 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
Camera list query
Event history
Alert rule creation
Multi-turn conversation with vision AI
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 discoverCLI +GET /discover/onvif) - Event timeline UI — visual heatmap timeline at
/timelinewith 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)
License
MIT © 2026 arunrajiah
See LICENSE for the full text.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cctvql-1.0.3.tar.gz.
File metadata
- Download URL: cctvql-1.0.3.tar.gz
- Upload date:
- Size: 132.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bb7eaf0f1a29a6c56a1208836ceb04607dca130e50063e83251e9d136b3ab03
|
|
| MD5 |
0aa3d654591af535656a1fa189ecf3d6
|
|
| BLAKE2b-256 |
3a1916ecb0dc3183cd4d4bddd4e8f9a4a4a4564aae7630f6cad771a10380bf98
|
Provenance
The following attestation bundles were made for cctvql-1.0.3.tar.gz:
Publisher:
release.yml on arunrajiah/cctvql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cctvql-1.0.3.tar.gz -
Subject digest:
4bb7eaf0f1a29a6c56a1208836ceb04607dca130e50063e83251e9d136b3ab03 - Sigstore transparency entry: 1344995687
- Sigstore integration time:
-
Permalink:
arunrajiah/cctvql@8cc2a7aacf9d9bfcc6f2a40dac8500c22cc15a23 -
Branch / Tag:
refs/tags/v1.0.3 - Owner: https://github.com/arunrajiah
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8cc2a7aacf9d9bfcc6f2a40dac8500c22cc15a23 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cctvql-1.0.3-py3-none-any.whl.
File metadata
- Download URL: cctvql-1.0.3-py3-none-any.whl
- Upload date:
- Size: 114.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
055d493f06e4d5fd47e655fa4f155681e9605a60fb846a6dd43ba70addda3fc0
|
|
| MD5 |
c9c6668dc077440f7bb2c9d3d5860646
|
|
| BLAKE2b-256 |
ccc841a2ac93f5452d7e48c296989bcde4078af8742b57c57de51ea545f98e5f
|
Provenance
The following attestation bundles were made for cctvql-1.0.3-py3-none-any.whl:
Publisher:
release.yml on arunrajiah/cctvql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cctvql-1.0.3-py3-none-any.whl -
Subject digest:
055d493f06e4d5fd47e655fa4f155681e9605a60fb846a6dd43ba70addda3fc0 - Sigstore transparency entry: 1344995731
- Sigstore integration time:
-
Permalink:
arunrajiah/cctvql@8cc2a7aacf9d9bfcc6f2a40dac8500c22cc15a23 -
Branch / Tag:
refs/tags/v1.0.3 - Owner: https://github.com/arunrajiah
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8cc2a7aacf9d9bfcc6f2a40dac8500c22cc15a23 -
Trigger Event:
push
-
Statement type: