Back to blog
FILE 0xD5·THROTTLING DOORBELL NOTIFICATIONS TO THE ONES THAT MATTER

Throttling doorbell notifications to the ones that matter

Back to blog
FILE 0xD5·THROTTLING DOORBELL NOTIFICATIONS TO THE ONES THAT MATTER
Back to blog
FILE 0xD5·THROTTLING DOORBELL NOTIFICATIONS TO THE ONES THAT MATTER
May 10, 2026 · homelab, notifications

I rolled out face-recognition notifications on my front-door doorbell and got spammed with hundreds of pushes in a few hours. I killed the cron, audited the logic, and rewrote the dedup rule. The fix is short. The lesson is shorter.

What was happening

The notifier was firing on every face-recognition row that flipped state. "Recognized Chester" → push. "Recognized Chester again on the next frame" → another push. "Recognized Chester in the same video, frame 4" → another. Multiply by every motion event in a busy day and the phone became unusable.

The first thing I did was just stop the bleeding: comment out the cron entry with a tag I could grep for later, kill the running process. The poller and the face-recognition worker stayed running so events kept flowing into storage. Only the user-facing push was off.

#DISABLED-2026-05-10  */1 * * * * /opt/.../cass_ring_notify.py

The #DISABLED- prefix is a habit worth keeping. It's easy to grep for ("what cron entries did I disable and why?") and easy to re-enable by deleting six characters.

What I found

The notifier had no concept of "an event." Every row that crossed a state threshold fired a notification, and a single doorbell event produces a handful of rows as the worker samples frames. There was also no concept of "this class of recognition is interesting." A recognized known person was treated as just as alert-worthy as an unknown stranger, which is the exact inverse of what I want.

Two specific bugs:

  • No per-event dedup. The right granularity is one Ring video = at most one notification, regardless of how many rows that video produces.
  • No status filter. Identified faces should not produce a user-visible push at all by default. Unknown faces should.

The fix

Rewrote the notifier to fire only on rows where fr_status = 'unknown' and set a notified flag on the row after the push. The row-level flag is the per-event dedup: each video produces exactly one row eligible for notification (the row representing the strongest unknown match), and once it's been notified it doesn't fire again.

def maybe_notify(rows):
    for row in rows:
        if row["fr_status"] != "unknown":
            continue
        if row.get("notified"):
            continue
        send_push(f"Unknown person at the front door at {row['ts']}")
        row["notified"] = True
        save(row)

Then I drained the backlog before re-enabling cron, marking the older stuck rows as skipped-backlog-<date> so the new notifier wouldn't spam them all out as a single restart artifact.

What I'd do differently

Notification systems are hard to design after the fact. The right mental model from day one is: every notification you send costs the user some attention; every notification you suppress costs them some trust. The default should be silent. Add a category, observe its real-world fire rate for a week, then decide if it's worth notifying on. If I'd done that, I would never have shipped "notify on recognized known faces" — because nobody actually wants their phone to buzz when their own front door sees them.