Metadata-Version: 2.4
Name: cctvql
Version: 1.0.2
Summary: Conversational query layer for CCTV systems — ask your cameras anything in plain English
Author-email: arunrajiah <arunrajiah@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/arunrajiah/cctvql
Project-URL: Repository, https://github.com/arunrajiah/cctvql
Project-URL: Issues, https://github.com/arunrajiah/cctvql/issues
Project-URL: Documentation, https://github.com/arunrajiah/cctvql/wiki
Keywords: cctv,nlp,surveillance,frigate,onvif,llm,conversational-ai,nvr,voice,whisper,speech-to-text
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Requires-Dist: pyyaml>=6.0
Requires-Dist: fastapi>=0.111
Requires-Dist: uvicorn[standard]>=0.30
Requires-Dist: pydantic>=2.0
Requires-Dist: websockets>=12.0
Requires-Dist: python-multipart>=0.0.12
Requires-Dist: aiosqlite>=0.20
Provides-Extra: mqtt
Requires-Dist: paho-mqtt>=2.0; extra == "mqtt"
Provides-Extra: onvif
Requires-Dist: onvif-zeep>=0.2.12; extra == "onvif"
Provides-Extra: voice
Requires-Dist: openai>=1.0; extra == "voice"
Requires-Dist: faster-whisper>=1.0; extra == "voice"
Provides-Extra: notifications
Requires-Dist: aiosmtplib>=3.0; extra == "notifications"
Provides-Extra: all
Requires-Dist: cctvql[mqtt,notifications,onvif,voice]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-mock>=3.12; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: respx>=0.20; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: pre-commit>=3.7; extra == "dev"
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
Dynamic: license-file

<div align="center">

# cctvQL

**Ask your CCTV system anything in plain English.**

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://python.org)
[![CI](https://github.com/arunrajiah/cctvql/actions/workflows/ci.yml/badge.svg)](https://github.com/arunrajiah/cctvql/actions)
[![codecov](https://img.shields.io/badge/coverage-64%25-yellow)](https://github.com/arunrajiah/cctvql/actions)
[![PyPI](https://img.shields.io/pypi/v/cctvql)](https://pypi.org/project/cctvql/)
[![Docker](https://img.shields.io/badge/docker-ready-blue)](https://hub.docker.com/r/arunrajiah/cctvql)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
[![CodeQL](https://github.com/arunrajiah/cctvql/actions/workflows/codeql.yml/badge.svg)](https://github.com/arunrajiah/cctvql/actions/workflows/codeql.yml)

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](#quick-start) · [Documentation](#documentation) · [Adapters](#supported-systems) · [Contributing](#contributing)

</div>

---

## 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

<div align="center">

**Web UI — landing**
![cctvQL landing screen showing camera sidebar and example query chips](docs/screenshots/01_landing.png)

**Camera list query**
![Response showing all 4 cameras with status and zones](docs/screenshots/02_camera_list.png)

**Event history**
![Recent motion events on the Front Door camera with timestamps and confidence scores](docs/screenshots/03_events.png)

**Alert rule creation**
![Natural language alert setup — person detected after 10pm](docs/screenshots/04_alert_setup.png)

**Multi-turn conversation with vision AI**
![Multi-turn conversation including a GPT-4o Vision analysis of an event snapshot](docs/screenshots/05_conversation.png)

</div>

---

## Quick Start

### Try it now — no hardware needed

```bash
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)

```bash
# 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

```bash
pip install cctvql

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

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

### From source

```bash
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](docs/configuration.md) |
| REST API reference | [docs/api.md](docs/api.md) |
| Notifications setup | [docs/notifications.md](docs/notifications.md) |
| Session & event persistence | [docs/persistence.md](docs/persistence.md) |
| Writing an adapter | [docs/adapters.md](docs/adapters.md) |
| LLM backend setup | [docs/llm-backends.md](docs/llm-backends.md) |
| Home Assistant integration | [docs/home-assistant.md](docs/home-assistant.md) |
| Docker deployment | [docs/docker.md](docs/docker.md) |
| Frigate + cctvQL sidecar | [deploy/frigate-sidecar/](deploy/frigate-sidecar/) |
| Troubleshooting | [docs/troubleshooting.md](docs/troubleshooting.md) |

---

## Supported Systems

| System | Type | Adapter | Status |
|--------|------|---------|--------|
| [Frigate NVR](https://frigate.video) | NVR | `frigate` | ✅ Full support (REST + MQTT) — [sidecar template](deploy/frigate-sidecar/) |
| 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](https://www.synology.com/en-global/surveillance) | NVR | `synology` | ✅ Full support (Web API) |
| [Milestone XProtect](https://www.milestonesys.com/) | Enterprise NVR | `milestone` | ✅ Full support (REST API) |
| [Scrypted](https://scrypted.app) | Smart home NVR | `scrypted` | ✅ Full support (Bearer token) |
| **Your system** | Any | — | [Write an adapter!](docs/adapters.md) |

> Writing an adapter takes ~100 lines. See the [adapter guide](docs/adapters.md).

---

## Supported LLM Backends

| Backend | Privacy | Cost | Quality |
|---------|---------|------|---------|
| [Ollama](https://ollama.com) (llama3, mistral, phi3…) | 🔒 100% local | Free | ⭐⭐⭐⭐ |
| OpenAI (gpt-4o-mini, gpt-4o) | ☁️ Cloud | Paid | ⭐⭐⭐⭐⭐ |
| Anthropic (claude-haiku, claude-sonnet) | ☁️ Cloud | Paid | ⭐⭐⭐⭐⭐ |
| [LM Studio](https://lmstudio.ai) | 🔒 Local | Free | ⭐⭐⭐⭐ |
| Any OpenAI-compatible API | Varies | Varies | Varies |

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

---

## Configuration

```yaml
# 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):
```bash
export CCTVQL_DB_PATH=/data/cctvql.db
```

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

---

## REST API

```bash
# 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](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.

```bash
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](CONTRIBUTING.md) for the full guide.

```bash
# 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

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

---

## License

MIT © 2026 [arunrajiah](https://github.com/arunrajiah)

See [LICENSE](LICENSE) for the full text.

---

<div align="center">
If cctvQL is useful to you, please ⭐ the repo — it helps others find it!
</div>
