Inspiration

Small businesses like cafés and restaurants get supplier deliveries several times a week. Drivers drop boxes at the back door, nobody has time to count them, and short or missing orders are discovered days later with no proof to dispute them. Many of these businesses already have a Ring camera pointed at that door. I wanted to see whether that camera could do the receiving job, not just security.

What it does

Dockhand turns a Ring camera into an automatic receiving clerk. When a delivery happens, it:

  1. Captures live video from the Ring camera and picks the clearest frames
  2. Uses Amazon Nova Pro on Bedrock to read the scene: carrier, box count, visible label text
  3. Logs the delivery with a snapshot as evidence
  4. Matches it against the orders the business expected, and flags short, over, missing, and unexpected deliveries
  5. Produces a one-click dispute summary the owner can send to the supplier

The dashboard shows today's ledger, expected orders, discrepancies with evidence, and a supplier scorecard that reveals which supplier keeps shorting you. Dockhand is also an MCP server (spec 2025-11-25), so assistants can answer questions like "Did the Sysco delivery come today?", which I demo with a simulated Alexa+ assistant page.

How I built it

  • Ring Partner API: device discovery, and live view over WebRTC/WHEP decoded with aiortc. Events come in through HMAC-verified webhooks, an Event History poller, or a manual trigger (I found no way to get webhooks from the Playground). A Postgres-backed queue dedupes replays.
  • Smart capture: instead of a fixed timeout, capture stops once the scene has settled, measured by the most-changed patch of a downscaled frame (a small, distant car barely moves the frame average).
  • Vision extraction: the top 3 of 10 sampled frames (scored on sharpness and change) go to Amazon Bedrock, which returns validated JSON.
  • Reconciliation: matching deliveries to orders is an assignment problem (scipy linear_sum_assignment) over carrier, time window, and box count, with explicit "leave unmatched" options so the solver never sacrifices a good match to force two bad ones.
  • Stack: Python, FastAPI, asyncio, PostgreSQL, Next.js + TypeScript, the MCP Python SDK, Docker Compose. 414 Python tests + 33 web tests, CI on every PR.

Results (every number with its sample size)

What Result
Exact box count, real doorbell/security camera images 92.9% (26/28), Nova Pro
Exact box count, real hand-held door photos 10/11, Nova Pro
False alarms 1 of 20 empty Porch Piracy scenes
Nova Pro vs Claude Sonnet 4.6 (real camera images) No measurable accuracy difference (26/28 vs 25/28; 10/11 vs 11/11); Nova 2.2–2.4× faster and 3.5–4.6× cheaper on the image datasets, so Nova is the default
Event → logged delivery 33 s → 12.5–14.7 s (2.3–2.7× faster, N=2 live runs each)
Discrepancy detection (simulated) 95.9% precision (424/442), 94.4% recall (424/449)
MCP conformance 5/5 applicable official checks

On synthetic multi-box composites, Sonnet was clearly ahead (27/36 vs 15/36), which motivates the model cascade below.

Eval data: Porch Piracy (CC BY 4.0) and Packages (Public Domain) from Roboflow Universe, MPJ Package Detection (CC BY 4.0) for the synthetic composites, and Ring Playground footage. Selection rules were committed before any model ran. Supplier history in the demo is clearly labeled as simulated.

Challenges

  • The Ring Playground behaves differently from the docs: offline devices accepted live-view sessions but sent no video, the Event History filter was ignored, and clips ended unpredictably with no end signal. I recorded each finding as a test fixture and logged 18 friction entries.
  • Bedrock access: on a new Free-plan account, an organization policy blocked Bedrock API keys, and some listed models couldn't be invoked. I switched to SigV4 credentials and probed models before trusting them.
  • An honest eval: my first synthetic test images had a design flaw that undercounted boxes. I caught it, documented it, and fixed the 16 affected images under a written amendment before re-running. I report those as a second build, not a held-out result.

What I learned

Measuring before optimizing pays off: the latency breakdown showed 25 of the 33 seconds were a fixed capture timeout, not real work. Benchmarking two models on the same data turned a guess into a defensible cost decision. And documenting where an API surprised me was as valuable as the code.

What's next

  • Validate on real delivery video (the motion threshold is tuned on Playground clips only)
  • Download recorded clips for late events instead of skipping them
  • A model cascade: fast Nova first, escalating to Sonnet for multi-box scenes
  • A Ring Appstore listing for small businesses

Built With

Share this project:

Updates

Submission history