Multiple Scenescape Deployment#

Smart NVR with Scenescape running on separate machines: Smart Intersection (SI) on System 1, NVR stack on System 2. For single-node deployment, see Integrate Scenescape with Smart NVR.

Overview#

Smart NVR maintains a persistent, independent MQTT connection to each SI node. Events are tagged with the broker id to route them to the correct Frigate camera.

  • One entry per SI node. In brokers.yaml, host is the MQTT broker IP used at runtime; rtsp_host and rtsp_port are the RTSP stream IP and port, read only by setup.sh.

  • Camera naming. Frigate cameras must be named {broker_id}-camera{n} (e.g. si1-camera1). The broker id must match this prefix exactly. The id can use any prefix (si1, intersection_1, …) as long as it ends with the SI node number, a number from 1 to 20, which setup.sh uses to pair the node with its RTSP settings.

  • Brokers persist. On startup, the broker manager reads brokers.yaml, seeds Redis, and starts one connection per enabled broker. The API persists changes back to this file.

Prerequisites#

  • VSS must be running and reachable from System 2.

  • See system requirements for hardware prerequisites.

Configuration#

brokers.yaml#

Edit resources/broker-config/brokers.yaml before starting, or manage brokers at runtime via the API.

# resources/broker-config/brokers.yaml
brokers:
  - id: si1                          # Must match Frigate camera prefix: si1-camera*
    name: Smart Intersection 1
    host: <si1_mqtt_ip>               # MQTT broker IP
    port: 1883
    topic: scenescape/data/camera/#
    type: scenescape
    throttle_interval: 2.0
    enabled: true
    rtsp_host: <si1_rtsp_ip>          # RTSP source IP, read by setup.sh
    rtsp_port: 8554                   # RTSP source port, read by setup.sh

  - id: si2
    name: Smart Intersection 2
    host: <si2_mqtt_ip>
    port: 1883
    topic: scenescape/data/camera/#
    type: scenescape
    throttle_interval: 2.0
    enabled: true
    rtsp_host: <si2_rtsp_ip>
    rtsp_port: 8556                   # Nodes can share an IP on different ports

TLS is enabled by default. Broker connections do not use username or password authentication.

Broker fields#

Field

Required

Default

Description

id

✅

—

Unique identifier. Must match the Frigate camera name prefix (e.g. si1 → si1-camera*) and end in the node number, 1 to 20 (e.g. si1, intersection_1).

name

✅

—

Human-readable label.

host

✅

—

MQTT broker IP address.

topic

✅

—

MQTT topic to subscribe to.

port

—

1883

MQTT broker port.

type

—

scenescape

Event type. Always scenescape for SI nodes.

use_tls

—

true

Enable TLS. Set to false for plain MQTT brokers.

throttle_interval

—

2.0

Minimum seconds between processed events.

enabled

—

true

Set to false to disable on startup without removing.

rtsp_host

—

—

RTSP stream IP. Read by setup.sh to generate Frigate cameras; unused at runtime.

rtsp_port

Required for start-nvr

—

RTSP stream port (1–65535). Read by setup.sh start-nvr to generate Frigate cameras; unused at runtime.

Environment variables#

Variable

Required

Default

Description

NVR_SCENESCAPE

✅

—

Must be true to enable Scenescape mode.

VSS_IP

✅

—

VSS service IP. The single nginx proxy serves both summary and search.

VSS_PORT

—

12345

VSS service port.

MQTT_USER

—

auto-generated

Local Mosquitto username (Frigate ↔ NVR).

MQTT_PASSWORD

—

auto-generated

Local Mosquitto password.

SI_RTSP_HOST

—

brokers.yaml

RTSP IP for si1. Overrides rtsp_host for si1. Ignored by start (single-node), which always uses this machine’s IP.

SI{N}_RTSP_HOST

—

brokers.yaml

RTSP IP for siN (N ≥ 2). Overrides rtsp_host for siN.

SI_RTSP_PORT

—

brokers.yaml

RTSP port for si1. Overrides rtsp_port for si1. Used by start-nvr only.

SI{N}_RTSP_PORT

—

brokers.yaml

RTSP port for siN (N ≥ 2). Overrides rtsp_port for siN. Used by start-nvr only.

SI_NODE_COUNT

—

highest node number in brokers.yaml

Number of SI nodes to generate Frigate cameras for (max 20).

RTSP_STREAM_PORT

—

8554

Local RTSP streamer port for start and start-si. Not used as a fallback by start-nvr.

SCENESCAPE_MQTT_BROKER

—

—

Legacy: seeds si1 MQTT broker into Redis on startup. Prefer brokers.yaml or the API.

BROKERS_CONFIG_PATH

—

resources/broker-config/brokers.yaml

Path to broker config file.

MAX_CONCURRENT_EVENTS

—

50

Maximum simultaneous in-flight event tasks.

BROKER_RECONNECT_DELAY

—

5.0

Seconds before reconnecting after a broker disconnect.

In the SI* variables, N is the node number: the trailing digits of the broker id (for example, intersection_2 is node 2, so SI2_RTSP_HOST applies to it).

Deployment#

System 1 — SI node(s)#

export NVR_SCENESCAPE=true
# export SI_RTSP_HOST=<external_rtsp_ip>  # optional: use an external RTSP source
# export RTSP_STREAM_PORT=<port>              # optional, default 8554
source setup.sh start-si

Downloads demo videos and starts a local MediaMTX RTSP streamer by default. Setting SI_RTSP_HOST to a remote IP skips the local streamer.

On exit, the script prints System 1’s IP and MQTT port — use these when adding the broker on System 2.

System 2 — NVR node#

Populate brokers.yaml with host and rtsp_host for each SI node before starting.

export NVR_SCENESCAPE=true
export VSS_IP=<ip>
export VSS_PORT=<port>              # optional, default 12345

# Optional: override brokers.yaml rtsp_host/rtsp_port for si1
# export SI_RTSP_HOST=<si1_rtsp_ip>
# export SI_RTSP_PORT=<si1_rtsp_port>

source setup.sh start-nvr

start-nvr reads the SI node count and RTSP IPs from brokers.yaml to generate the Frigate camera list, and connects to the brokers listed in it. Brokers can still be added later via POST /brokers/.

Note

Any SI node without an RTSP IP (rtsp_host or SI{N}_RTSP_HOST) is skipped with a warning. start-nvr exits with an error only if no node has one.

Stop#

source setup.sh stop-nvr   # System 2
source setup.sh stop-si    # System 1

If a local RTSP streamer is running on System 1, stop-si prompts:

Local RTSP streamer is running. Stop it too? [y/N]

Respond y to stop it, or n to leave it running (source setup.sh stop-streamer stops it independently).

RTSP Streamer#

To manage the MediaMTX RTSP streamer on System 1 independently of SI services. start-streamer downloads demo videos if not already present, then starts the streamer.

source setup.sh start-streamer
source setup.sh stop-streamer

Managing brokers at runtime#

The /brokers/ API modifies live broker connections without restarting the stack. Changes persist to brokers.yaml automatically.

BASE=http://localhost:8000

# List brokers
curl $BASE/brokers/

# Add a broker (starts MQTT connection immediately)
curl -X POST $BASE/brokers/ \
  -H "Content-Type: application/json" \
  -d '{
    "id": "si3",
    "name": "Smart Intersection 3",
    "host": "<si3_mqtt_ip>",
    "topic": "scenescape/data/camera/#",
    "rtsp_host": "<si3_rtsp_ip>",
    "rtsp_port": 8557
  }'

# Update a broker (restarts its MQTT connection)
curl -X PUT $BASE/brokers/si3 \
  -H "Content-Type: application/json" \
  -d '{
    "id": "si3",
    "name": "SI 3 updated",
    "host": "<si3_ip_updated>",
    "topic": "scenescape/data/camera/#",
    "enabled": true
  }'

# Remove a broker (stops its MQTT connection)
curl -X DELETE $BASE/brokers/si3

Adding a broker via the API updates MQTT routing only. To record video from a new SI node, set its rtsp_host, then re-run setup.sh start-nvr to regenerate Frigate camera blocks.

Frigate camera configuration#

setup.sh generates resources/frigate-config/config.yml at startup. It reads the SI node count from brokers.yaml (highest siN id), then appends 4 camera blocks per node ({broker_id}-camera1 through camera4):

SI node

RTSP IP source (in priority order)

RTSP port source (in priority order)

si1

SI_RTSP_HOST → rtsp_host → node skipped with a warning (start always uses this machine’s IP)

SI_RTSP_PORT → rtsp_port → start-nvr fails (start always uses RTSP_STREAM_PORT)

si2..siN

SI{N}_RTSP_HOST → rtsp_host → node skipped with a warning

SI{N}_RTSP_PORT → rtsp_port → start-nvr fails

Invalid SI_RTSP_PORT/SI{N}_RTSP_PORT values are ignored with a warning. If every node is skipped, start-nvr exits with an error. start-nvr also exits with an error if a resolved node (has an rtsp_host) has no rtsp_port from either source.

Verify integration#

# Confirm all broker tasks started
docker logs nvr-event-router | grep "subscribed to"
# Expected:
#   [si1] subscribed to scenescape/data/camera/# at <si1_ip>:1883
#   [si2] subscribed to scenescape/data/camera/# at <si2_ip>:1883

# Monitor live events
docker logs nvr-event-router -f | grep "Scenescape event"

# Check reconnection attempts
docker logs nvr-event-router | grep "reconnecting"

Troubleshooting#

Broker connects but no events appear

  • Verify the broker id matches the Frigate camera prefix (e.g. id: si2 → si2-camera1..4).

  • Confirm SI is publishing to scenescape/data/camera/#.

Ignoring broker id '...': it must end with a node number

The broker id in brokers.yaml must end with a number from 1 to 20 (for example si1 or intersection_2). An id without a trailing number is skipped, and setup.sh start-nvr then fails with No SI node has a resolvable rtsp_host. A number outside 1–20 is also ignored. Rename the id (for example intersection → intersection_1) and re-run setup.sh start-nvr.

[siN] connection error: [Errno 111] Connect call failed

MQTT port unreachable. The broker manager retries automatically. Check connectivity:

nc -zv <siN_host> 1883

Frigate cameras show no recordings

RTSP IPs are written into config.yml at startup. If they changed, re-run setup.sh start-nvr to regenerate the config.

UI shows no Scenescape source

docker exec nvr-event-router-ui env | grep NVR_SCENESCAPE

Confirm NVR_SCENESCAPE=true is exported in the shell running setup.sh start-nvr.

For general issues, see the Troubleshooting Guide.