Surveillance Module
CCTV integration, motion detection, clip recording, and evidence linking — server-side FFmpeg detection, browser overlay, and Nextcloud storage.
Last updated: 2025-02-18
Surveillance Module
The Surveillance module connects CCTV cameras to the evidence chain: live streams, motion-triggered clip recording, optional browser-based object detection, and automatic linking to WHMS events and the Time Machine.
Camera Grid Overview
The 2x3 surveillance grid provides at-a-glance monitoring of all warehouse cameras. Click any feed to expand and view detailed metadata.
6 camera feeds with recording status, motion detection indicators, and zone assignments.
Architecture
Design principles
- Server-side first: Motion detection and clip extraction run in FFmpeg; the browser shows streams and controls.
- Evidence-linked: Clips are uploaded to Nextcloud with metadata (camera, time, duration) and can be attached to WHMS placement events.
- Optional browser detection: TensorFlow.js COCO-SSD runs in the browser for object boxes only; no frames leave the device.
Component inventory
All surveillance-related components and their roles.
Motion detection & control
| Component | Path | Description |
|---|---|---|
MotionClipControl | @/components/iot/motion-clip-control | Start/stop detection per camera, sensitivity, pre/post record |
DetectionControlPanel | @/components/iot/detection-control-panel | Multi-camera detection toggles and settings |
DetectionConfigPanel | @/components/iot/detection-config-panel | Rules and taxonomy for detection |
DetectionDashboard | @/components/iot/detection-dashboard | Overview of all cameras and motion state |
DetectionToast | @/components/iot/detection-toast | In-app toast when motion is detected |
Streams & playback
| Component | Path | Description |
|---|---|---|
CameraGrid | @/components/iot/camera-grid | Grid of live camera streams (HLS) |
EnhancedStreamPlayer | @/components/iot/enhanced-stream-player | Single stream with controls and overlay |
EnhancedMotionViewer | @/components/iot/enhanced-motion-viewer | Stream + motion controls + optional TF overlay |
StreamPreview | @/components/iot/stream-preview | Small preview tile |
CctvStreamField | @/components/iot/cctv-stream-field | Table/form field for selecting and showing a stream |
Browser detection overlay
| Component | Path | Description |
|---|---|---|
BrowserDetectionOverlay | @/components/iot/browser-detection-overlay | TensorFlow.js COCO-SSD overlay on video |
DetectionOverlay | @/components/iot/detection-overlay | Wrapper for detection overlay |
BoundingBoxOverlay | @/components/iot/bounding-box-overlay | Draws bounding boxes and labels |
Clips & recordings
| Component | Path | Description |
|---|---|---|
ClipRecorder | @/components/iot/clip-recorder | Records from stream to blob/clip |
ClipExportDialog | @/components/iot/clip-export-dialog | Export clip to Nextcloud or download |
SaveClipDialog | @/components/iot/save-clip-dialog | Save clip with metadata |
RecordingsList | @/components/iot/recordings-list | List of saved clips with playback |
SurveillanceTimeline | @/components/iot/surveillance-timeline | Timeline of motion events and clips |
WHMS integration
| Component | Path | Description |
|---|---|---|
ZoneCCTVTab | @/components/whms/ZoneCCTVTab | CCTV feeds for a WHMS zone |
CCTVTab | @/components/whms/CCTVTab | Camera list and status in WHMS context |
CCTVWidget | @/components/workflow/widgets/CCTVWidget | Workflow widget for camera stream |
SurveillanceWidget | @/components/dashboard/SurveillanceWidget | Dashboard widget for surveillance |
Configuration & modals
| Component | Path | Description |
|---|---|---|
CameraQuickSettings | @/components/iot/camera-quick-settings | Quick settings for a camera |
CameraSettingsModal | @/components/iot/camera-settings-modal | Full camera configuration |
IotCameraSelector | @/components/iot/IotCameraSelector | Pick a camera from list |
EventViewer | @/components/iot/event-viewer | View detection events |
EventDetailModal | @/components/iot/event-detail-modal | Single event details and clip |
Fields & deck
| Component | Path | Description |
|---|---|---|
CCTVField | @/components/fields/CCTVField | Field renderer for CCTV reference |
IotEvidenceField | @/components/fields/IotEvidenceField | IoT evidence attachment |
DeckIntegrationPanel | @/components/iot/deck-integration-panel | Link detection to Deck cards |
SessionToRecordDialog | @/components/iot/session-to-record-dialog | Attach session to table record |
UI patterns (sandboxes)
These sandboxes show the UI building blocks used in the surveillance UI. Real components use the same primitives plus video elements and API state.
Camera status card
Single camera in a grid or list: status badge, name, and actions.
<Card className="max-w-xs"> <CardHeader className="pb-2 flex flex-row items-center justify-between"> <CardTitle className="text-sm font-medium">Loading dock — Cam 3</CardTitle> <Badge variant="secondary">Live</Badge> </CardHeader> <CardContent className="space-y-3"> <div className="aspect-video rounded-md bg-muted flex items-center justify-center text-xs text-muted-foreground"> Stream preview </div> <div className="flex gap-2"> <Button size="sm" variant="outline">Motion on</Button> <Button size="sm" variant="ghost">Settings</Button> </div> </CardContent> </Card>
Motion control strip
Start/stop detection and sensitivity (mirrors MotionClipControl).
<Card className="max-w-md"> <CardHeader className="pb-2"> <CardTitle className="text-base">Motion detection</CardTitle> <CardDescription>Camera 3 — Loading dock</CardDescription> </CardHeader> <CardContent className="space-y-4"> <div className="flex items-center justify-between"> <span className="text-sm">Status</span> <Badge variant="destructive">Recording</Badge> </div> <div className="flex gap-2"> <Button size="sm">Stop</Button> <Button size="sm" variant="outline">Sensitivity</Button> <Button size="sm" variant="outline">Clip buffer</Button> </div> <Alert> <AlertTitle>Motion detected</AlertTitle> <AlertDescription>Last event 30s ago. Clip saved.</AlertDescription> </Alert> </CardContent> </Card>
Clip list row
One row in the recordings list: thumbnail, time, duration, actions.
<div className="flex items-center gap-4 rounded-lg border p-3 max-w-lg"> <div className="w-24 h-14 rounded bg-muted flex-shrink-0 flex items-center justify-center text-xs text-muted-foreground"> Thumb </div> <div className="flex-1 min-w-0"> <p className="text-sm font-medium">Cam 3 — Loading dock</p> <p className="text-xs text-muted-foreground">14:32:05 · 12s</p> </div> <div className="flex gap-1"> <Button size="sm" variant="ghost">Play</Button> <Button size="sm" variant="ghost">Export</Button> </div> </div>
Detection alert panel
In-app alert when motion is detected (DetectionToast style).
<Alert variant="default" className="max-w-md"> <AlertTitle>Motion detected — Cam 2</AlertTitle> <AlertDescription> Staging area. Clip recorded and linked to evidence. Open to view. </AlertDescription> <div className="mt-2 flex gap-2"> <Button size="sm">View clip</Button> <Button size="sm" variant="outline">Dismiss</Button> </div> </Alert>
Camera grid placeholder
Grid of camera placeholders (real grid uses HLS players).
<div className="grid grid-cols-2 gap-2 max-w-md"> <div className="aspect-video rounded-lg border bg-muted flex items-center justify-center text-xs text-muted-foreground">Cam 1</div> <div className="aspect-video rounded-lg border bg-muted flex items-center justify-center text-xs text-muted-foreground">Cam 2</div> <div className="aspect-video rounded-lg border bg-muted flex items-center justify-center text-xs text-muted-foreground">Cam 3</div> <div className="aspect-video rounded-lg border bg-muted flex items-center justify-center text-xs text-muted-foreground">Cam 4</div> </div>
Surveillance dashboard strip
Summary strip: cameras online, motion active, recent clips (SurveillanceWidget style).
<div className="flex flex-wrap gap-4 rounded-lg border p-4 max-w-lg"> <div> <p className="text-2xl font-semibold">4</p> <p className="text-xs text-muted-foreground">Cameras online</p> </div> <div> <p className="text-2xl font-semibold">2</p> <p className="text-xs text-muted-foreground">Motion active</p> </div> <div> <p className="text-2xl font-semibold">12</p> <p className="text-xs text-muted-foreground">Clips today</p> </div> <Button size="sm">Open surveillance</Button> </div>
API summary
| Endpoint | Method | Description |
|---|---|---|
/api/iot/cctv | GET | List cameras (filtered by ownership) |
/api/iot/detection/motion-stream | POST | Start/stop motion detection, set options |
/api/iot/detection/motion-stream?cctvId=N | GET | Status and stats for camera N |
/api/iot/detection-events | GET | List motion events with optional filters |
/api/iot/buffer/[cctvId]/clip | GET | Get clip from buffer (by time range) |
For full request/response shapes and webhooks, see REST API and Motion Detection.