Inspiration
Millions of older adults live alone, and their families live with a low, constant worry: what if something happened today and nobody knows? The usual answers — indoor cameras, wearables, daily check-in calls — feel intrusive, get forgotten, or get refused.
But most homes already have a sensor that sees the day's rhythm: the front door. Ring's AI already describes who is at the door and flags unusual visitors for security. We noticed nobody looks at the opposite signal — when something that normally happens doesn't. No morning trip out by 10:30, when it happens every single day, is exactly the moment a caregiver would want a gentle nudge.
So we built Cadence: peace of mind for a parent living alone, from the door they already use — no camera inside.
What it does
- Learns the household's rhythm from Ring doorbell event metadata only — times and types (person, delivery, motion). No video, no faces, no stored images.
- Re-checks the day every minute, so a missed routine is caught even when nothing happens at the door — which is precisely the case that matters.
- Explains every alert honestly, split into what we saw, what we think, and what we don't know, with a confidence score.
- Keeps the caregiver in charge — it suggests a check-in call, but never contacts anyone on its own.
- Flags door activity during quiet overnight hours, without ever guessing who caused it.
- A calm, accessible dashboard: a plain-language headline ("No morning activity yet today."), a 24-hour timeline of the day, alerts read aloud, keyboard and screen-reader support, dark mode, and phone layouts.
How we built it
Ingress. A Fastify server receives Ring Partner API webhooks, verifies the X-Signature HMAC-SHA256 over the raw body with a timing-safe compare, acknowledges immediately, and processes asynchronously. Events are de-duplicated by request ID, so retries are harmless.
const expected = Buffer.from(`sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`);
const given = Buffer.from(header);
return expected.length === given.length && timingSafeEqual(expected, given);
Learning the rhythm. Each routine (morning, midday delivery, evening) starts from a configured window. From history we take the earliest matching event per day — each event assigned to at most one routine, the closest window — and learn a window centred on what really happens:
$$ c = \operatorname{median}(t_d), \qquad \tau = \operatorname{clamp}!\left(\tfrac{\mathrm{IQR}(t_d)}{2} + 20,\; 30,\; 150\right), \qquad W = [\,c - \tau,\; c + \tau\,] $$
where \( t_d \) is the first matching minute-of-day on day \( d \). We also measure consistency — how reliably it happens:
$$ k = \frac{\text{days the routine was observed}}{\text{days of history}} $$
Deciding. Each minute, every routine is matched, pending, or missing (no match by the end of its window plus a 30-minute grace). A missing routine's confidence grows with how reliable the routine is and how late it now is:
$$ \text{confidence} = k \cdot \left(0.6 + 0.4 \cdot \min!\left(1, \frac{\text{minutes late}}{120}\right)\right) $$
High-importance routines escalate ("check-in suggested"); anything below \( 0.4 \) is marked "for your summary" and isn't read aloud, so an irregular habit never sets off an alarm.
Explaining. Every alert is an ExplainableAlert — a small, source-agnostic JSON schema we're releasing as open source (Apache-2.0). It separates observed facts from inferences, carries confidence and privacy posture (imageTtlS: 0), and marks every suggested action as requiring the caregiver's confirmation. The wording is deterministic by default; Amazon Bedrock (Claude via the Anthropic Bedrock SDK) can optionally rephrase the suggestion more warmly, grounded strictly in the structured facts, and falls back silently if it's unavailable — so the product never goes quiet or invents a fact.
Dashboard. React 19 + Vite, live over Server-Sent Events, with a small design system (tactile surfaces, Nunito typography, one palette for light and dark). We validated layouts at nine widths from 320 px to 1920 px with automated browser checks.
Shipping. A Ring-shaped simulator lets the full loop run with zero hardware, and a 20-test Vitest suite covers the engine. Cadence is deployed on AWS Lightsail behind Caddy (automatic HTTPS) and kept alive with pm2.
Challenges we ran into
- Absence makes no events. An event-driven system is blind to the thing we care about most: a day where nothing happens. We added a server-side evaluation tick, plus a controllable demo clock so a compressed demo day still advances in real time.
- Days have to end. An early version let yesterday's 07:40 trip satisfy today's morning routine, so after day one it could never detect anything. We added proper day rollover: finished days fold into history and the baseline relearns.
- Calm means not repeating yourself. Re-evaluating on every webhook re-emitted every alert with a fresh ID, so the dashboard stacked duplicates and read them aloud again. Stable per-day alert IDs and snapshot updates fixed it.
- Time zones and ordering. Local-time display, adjacent windows double-counting a single event, and a race where a stale fetch could overwrite fresher live events all needed careful fixes.
- Saying only what we know. It's tempting to write "Someone was at the door at 3:15." Cadence can't know that — so it says "There was door activity," and lists the unknowns explicitly.
- Real API surface. We modelled Ring's webhook payloads and signing from the developer docs and drove everything through a faithful simulator; field names need confirming against production before launch.
Accomplishments that we're proud of
- It catches the silent case. Cadence raises a calm, explained alert on a day when zero events arrive — the scenario most event-driven systems simply can't see.
- Privacy by design, not by policy. It's genuinely useful with door-event timing alone: no camera inside, no faces, no stored images, no identification.
- Honest alerts. Every alert separates what was seen from what is guessed and what is unknown, with a confidence score — and nothing is ever sent without the caregiver's say-so.
- A reusable open primitive. The ExplainableAlert schema works for any smart-home source, not just Ring.
- Shipped end to end on AWS. A live HTTPS deployment with signed-webhook verification, protected admin endpoints, and a demo that resets itself so it's always ready to show.
- Built for real people. Read-aloud alerts, keyboard and screen-reader support, dark mode, and layouts checked from 320 px phones to 1920 px desktops.
- Tested and deterministic. 20 automated tests cover learning, deviation, day rollover, time zones, de-duplication and ordering.
What we learned
- For well-being, absence is the signal — and it needs a clock, not just an event stream.
- Explainability is a UX feature, not a log line. Separating saw / think / don't know made alerts feel trustworthy rather than alarming.
- Deterministic first, LLM second. Rules produce the facts; the model only polishes wording. That keeps the system testable and honest.
- Privacy can be the product, not a constraint: timing metadata alone is enough to be genuinely useful.
What's next for Cadence
- Connect to real Ring Partner API accounts and confirm payload shapes end to end.
- Durable storage (DynamoDB) and multi-household support.
- Caregiver notifications (SMS / push) with explicit opt-in, plus a weekly rhythm summary.
- Weekday/weekend and seasonal rhythms, and more sources feeding the same open ExplainableAlert schema.
Built With
- amazon-bedrock
- amazon-lightsail
- amazon-web-services
- claude
- fastify
- node.js
- react
- ring
- typescript
- vite
- webhooks
Log in or sign up for Devpost to join the conversation.