Initial commit: Vigilar DIY home security system
Phase 1 (Foundation): project skeleton, TOML config + Pydantic validation, MQTT bus wrapper, SQLite schema (9 tables), Click CLI, process supervisor. Phase 2 (Camera): RTSP capture via OpenCV, MOG2 motion detection with configurable sensitivity/zones, adaptive FPS recording (2fps idle/30fps motion) via FFmpeg subprocess, HLS live streaming, pre-motion ring buffer. Phase 3 (Web UI): Flask + Bootstrap 5 dark theme, 6 blueprints, Jinja2 templates (dashboard, kiosk 2x2 grid, events, sensors, recordings, settings), PWA with service worker + Web Push, full admin settings UI with config persistence. Remote Access: WireGuard tunnel configs, nginx reverse proxy with HLS caching + rate limiting, bandwidth-optimized remote HLS stream (426x240 @ 500kbps), DO droplet setup script, certbot TLS. 29 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
63
tests/unit/test_motion.py
Normal file
63
tests/unit/test_motion.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Tests for motion detection."""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from vigilar.camera.motion import MotionDetector
|
||||
|
||||
|
||||
def test_no_motion_on_static_scene():
|
||||
detector = MotionDetector(sensitivity=0.7, min_area_px=100, resolution=(160, 90))
|
||||
|
||||
# Feed identical frames to build background model
|
||||
static_frame = np.full((360, 640, 3), 128, dtype=np.uint8)
|
||||
for _ in range(80): # exceed warmup
|
||||
detected, rects, conf = detector.detect(static_frame)
|
||||
|
||||
# After warmup, static scene should have no motion
|
||||
detected, rects, conf = detector.detect(static_frame)
|
||||
assert not detected
|
||||
assert len(rects) == 0
|
||||
|
||||
|
||||
def test_motion_detected_on_scene_change():
|
||||
detector = MotionDetector(sensitivity=0.9, min_area_px=50, resolution=(160, 90))
|
||||
|
||||
# Build background with static scene
|
||||
static = np.full((360, 640, 3), 128, dtype=np.uint8)
|
||||
for _ in range(80):
|
||||
detector.detect(static)
|
||||
|
||||
# Introduce a large change
|
||||
changed = static.copy()
|
||||
changed[50:200, 100:300] = 255 # large white rectangle
|
||||
|
||||
detected, rects, conf = detector.detect(changed)
|
||||
assert detected
|
||||
assert len(rects) > 0
|
||||
assert conf > 0
|
||||
|
||||
|
||||
def test_sensitivity_update():
|
||||
detector = MotionDetector(sensitivity=0.5)
|
||||
assert detector._sensitivity == 0.5
|
||||
|
||||
detector.update_sensitivity(0.9)
|
||||
assert detector._sensitivity == 0.9
|
||||
|
||||
# Clamps to valid range
|
||||
detector.update_sensitivity(1.5)
|
||||
assert detector._sensitivity == 1.0
|
||||
detector.update_sensitivity(-0.1)
|
||||
assert detector._sensitivity == 0.0
|
||||
|
||||
|
||||
def test_reset_clears_background():
|
||||
detector = MotionDetector(resolution=(80, 45))
|
||||
frame = np.zeros((360, 640, 3), dtype=np.uint8)
|
||||
|
||||
for _ in range(80):
|
||||
detector.detect(frame)
|
||||
|
||||
assert detector._frame_count >= 80
|
||||
detector.reset()
|
||||
assert detector._frame_count == 0
|
||||
Reference in New Issue
Block a user