The biggest weakness of fixed-threshold motion detection is that no single number is right for every camera at every time of day. A driveway at noon is a different problem than the same driveway at 3 AM. So I stopped using one number.
What it does now
Two changes work together:
- Percentile floor over a decaying histogram. The detector keeps a
rolling distribution of the last 2 hours of motion-blob fractions and uses the 97th percentile as its "what's quiet here" floor. If wind has been moving leaves all night, the threshold rises to compensate. When the wind stops, the threshold falls again over the next hour.
- Diurnal EMA. A separate exponentially-weighted moving average tracks
the scene's typical noise by time of day. 3 AM and 3 PM get different baselines. The EMA learns over weeks.
Both values persist to the database (migration 0016) so the learning survives recorder restarts.
What it killed
Whole categories of false triggers, especially the slow-burn ones: tree shadows moving across a wall as the sun rises, headlight glare from passing cars, distant traffic on a far street. The detector adapts instead of demanding I hand-tune each camera.
Full design: docs/MOTION-ADAPTIVE-THRESHOLD.md. Tuning UI lives at Settings → Motion tuning in the admin console.