opennvr-app-sdk
The SDK for building vision apps on OpenNVR, the self-hosted AI video platform. Cameras, Tier-0 detections, pluggable inference, an event bus, recordings, an operator alert inbox, users and per-camera permissions, and a catalog every deployment opens — all already running. You write the rule, the model or the workflow; this package is the only import you need.
Apache-2.0 — ship your app under any licence, closed included, with no fee to OpenNVR. Why that holds even though the platform core is AGPL: LICENSING.md.
pip install opennvr-app-sdk
An app in one function
from opennvr_app_sdk import App
app = App("loitering", name="Loitering", version="1.0.0", category="perimeter")
app.param("dwell_s", float, default=30.0)
app.metric("alerted", label="Alerts fired")
@app.on_detection("person", zone="driveway", dwell="$dwell_s")
def loitering(event):
event.alert(f"Person loitering on {event.camera}", severity="high")
app.store["alerted"] = app.store.get("alerted", 0) + 1
if __name__ == "__main__":
raise SystemExit(app.run())
That app subscribes to the platform's detections, serves the registry
contract (/health, /state, /manifest, config form, actions),
registers itself in the App Catalog, is issued its own credential, and
fires alerts that reach the operator inbox — none of which you wrote.
The zone becomes an editor named driveway that the operator draws on a
camera still; dwell_s becomes a field in the config form they can
tune; alerted becomes a tile on the app's dashboard. The presence
timer, the once-per-episode latch, the alert envelope and the config
file are the SDK's.
Declaring a surface implements it: @app.action adds an operator
button, @app.ui an embedded page, @app.on_license the gate for a
paid app, @app.state / app.metric a dashboard, and inside a rule
event.publish() reaches other apps while event.nvr is the platform.
See it fire before you touch Docker:
opennvr-app new loitering # a runnable app + tests
opennvr-app dev # run it against a simulated camera
opennvr-app validate . # what a reviewer would check
opennvr-app spec # its OpenAPI 3.1 document
Your app self-describes: the contract server it gets for free serves
OpenAPI 3.1 at /openapi.json and AsyncAPI 3.0 at
/asyncapi.json, both generated from your manifest — a declared action
is a path with a typed body, a declared param is a schema. Point Swagger
UI or a client generator at a running app and it works.
The reference
opennvr.org/sdk — every export, in tiers, generated from these docstrings. Start at the front door: six names, and most apps need nothing else.
One example per class
cookbook/ is a runnable file per class — how it is
constructed or subclassed, and which APIs it uses. Every file is
imported and exercised by the test suite, so an example that references
a name the SDK no longer exports breaks in CI rather than misleading
you months later. Start at
01_app_facade.py.
…or the class underneath it
App compiles to a Detector. When a rule outgrows the decorators,
write the Detector directly — same process, same manifest, same
alerts:
from opennvr_app_sdk import Alert, AppManifest, Detector, Param, app
class Loitering(Detector):
manifest = AppManifest(
id="loitering", name="Loitering", version="1.0.0", category="perimeter",
requires_tasks=["object_detection"],
params=[Param("dwell_s", float, default=30.0)],
)
def setup(self):
self.present = self.keyed_state(ttl=10.0) # forgets a camera 10 s after the last person
def on_detections(self, camera_id, detections, event):
people = [d for d in detections if d.get("label") == "person"]
if not people:
return
rec = self.present.touch(camera_id)
if rec.age >= self.cfg.dwell_s and not rec.alerted:
rec.alerted = True
yield Alert(title="Loitering", description=f"{len(people)} person(s) for {rec.age:.0f}s",
camera_id=camera_id, severity="medium")
if __name__ == "__main__":
raise SystemExit(app(Loitering).run())
FrameApp drives its own inference, AlertSubscriber consumes the
alert bus and DomainEventSubscriber consumes contracted domain
events — same shape, different input.
The platform, from an app
from opennvr_app_sdk import OpenNVR # or opennvr_app_sdk.aio.AsyncOpenNVR
nvr = OpenNVR() # OPENNVR_URL + the app's key
for cam in nvr.cameras(): # only cameras assigned to this app
jpeg = nvr.snapshot(cam)
det = nvr.ai.infer("yolov8", jpeg, task="object_detection", camera_id=cam.handle)
nvr.state.set("last_seen", {"cam1": 12.5}) # durable, survives restarts
clips = nvr.recordings("cam1").list(start="2026-09-05T00:00:00Z")
mine = nvr.alerts.inbox(unacked=True)
Where to go next
- Developer program — the deal: no fee, your licence, the compatibility promise.
- Your first detector — scaffold, run against a stack, list in the catalog.
- SDK reference — every public name, by task.
- Platform client, app surfaces, credentials, event contracts.
- Example apps — thirteen first-party apps built on this SDK.
Compatibility: the app-registry contract is versioned (api_version)
and tested in the OpenNVR repository on every change; min_sdk_version
only moves when an older SDK would misbehave. Issues tagged sdk at
open-nvr/open-nvr.
Metadata
Release files for opennvr-app-sdk 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| opennvr_app_sdk-0.7.0.tar.gz | 260.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| opennvr_app_sdk-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 454.5 kB
Release files / opennvr_app_sdk-0.7.0.tar.gz
| Download URL | opennvr_app_sdk-0.7.0.tar.gz |
|---|---|
| Size | 260.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8d82528bfce1a6a52df55fa6121b7949322e604a8ba79a7fa9f2989174063fd2
|
|
BLAKE2b-256 checksum How to use checksums |
61cf70997fe63307026900f730f1eaf1a79b15bb0906952bb5fc9ef3df84f4a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / opennvr_app_sdk-0.7.0-py3-none-any.whl
| Download URL | opennvr_app_sdk-0.7.0-py3-none-any.whl |
|---|---|
| Size | 193.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cca2bbc78c11af71a105a220ae2a4db2a0333efebca2356bb616ed2026bd437e
|
|
BLAKE2b-256 checksum How to use checksums |
6ad83f4148bb649aee243cc3de61944c78fed972524dc6a84c95086543724a39
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency log