The house checks in, so family doesn't have to.
Still Here watches a Ring household the way a good neighbour would: it learns what an ordinary day looks like, notices when the day stops looking ordinary, and — this is the part that matters — knocks before it alarms. It plays a chime and treats any sign of life as an answer. Family is only pulled in when the home itself could not get one.
Built for the Build, Ship, Shape: Amazon Developer Hackathon, Ring track (priority categories: caretaking, accessibility).
Watch the demo | Devpost submission
Every "is mum okay?" product on the market solves the easy half — detect an anomaly, send a push notification. In a real household that design fails twice a week: she slept in, she was in the garden, the cat set off the hallway camera. After the third false alarm the family mutes the app, and the app is now worse than nothing.
So Still Here spends its first move on the home, not the phone:
| Phase | What happens | Who is disturbed |
|---|---|---|
calm |
rhythm matches history | nobody |
knocking |
chime plays on the Ring chime — "are you there?" | only the person at home |
answered |
any motion, doorbell press or door event after the chime | nobody — the incident closes silently |
escalated |
two unanswered knocks | family, with evidence |
The person at home answers by walking past a camera. No phone, no app, no button — which is exactly why it works for the people this is built for.
| Purpose | Endpoint |
|---|---|
| Learn the household rhythm | GET /v1/history/devices/{id}/events |
| Watch in real time | webhook v1.1 (motion_detected, button_press, device_offline), verified with X-Signature HMAC-SHA256 |
| Knock | POST /v1/devices/{id}/media/audio/playback (audio_ref, requires Chime Controls) |
| Evidence on escalation | POST /v1/devices/{id}/media/image/download — one still, only at escalation |
| Optional live check | POST /v1/devices/{id}/media/streaming/whep/sessions (WHEP/WebRTC) |
| Inventory & health | GET /v1/devices, /status, /capabilities, /configurations, GET /v1/users/me |
Devices are mapped to roles (front_door, indoor, chime) rather than model names, so
the engine reasons about "the front door opened", not about a particular Ring SKU.
npm install
npm run dev # http://localhost:3000That starts in sandbox mode: 14 days of synthetic history, a replayable day, and four
scenarios you can trigger from the dashboard. Every simulated event is POSTed to the real
/api/webhook/ring route with a real HMAC signature, so the demo exercises the same
verify → parse → engine path as production traffic.
- Open the Ring Developer Playground → Generate Token.
cp .env.example .env.localand paste the token intoRING_ACCESS_TOKEN.- Check what the account exposes:
npx tsx scripts/ring-probe.ts
curl -X POST localhost:3000/api/live— the dashboard reloads with real devices, real history and a rhythm learned from that household.
Playground tokens last about 30 minutes. For a long-running deployment use the OAuth refresh
token flow and set RING_WEBHOOK_SECRET so webhook signatures are enforced (they are
mandatory when NODE_ENV=production).
lib/rhythm/model.ts reduces event history to four things a caregiver can sanity-check on
the dashboard:
- first presence and last presence per local day → median and MAD (a single 3am bathroom trip must not redefine "morning")
- hourly presence probability → also gives us quiet hours, so the home never knocks at 4am
- p90 waking gap → how long this particular household normally goes quiet mid-day
lib/rhythm/engine.ts is a pure function of (state, events, model, now). It produces
knock / escalate / resolve actions and nothing else — no timers, no I/O — which is why
the escalation policy is covered by unit tests rather than hope:
npm test # 22 testsGuards that come from that test suite:
- never alerts on fewer than 7 learned days (
isConfident) - never knocks during learned quiet hours
- gives someone who left through the front door an away-grace before worrying
- ignores non-human motion (
sub_type: vehicle) as a sign of life - stands down even after escalating, if she walks past a camera at 11:03
- No continuous recording, no stored video. A single still is pulled only at escalation.
- The knock is a chime, not a microphone — nothing is listened to.
- Household state is a rhythm model plus one day of events: a few kilobytes, in memory, never sold, never sent anywhere except the family message.
- Webhook payloads are rejected unless the HMAC signature verifies.
The family message is assembled deterministically first (lib/notify/summary.ts) — the
concern, the last sign of life, what the home already tried. A model is then asked to
rephrase that evidence, in this order:
- Ollama on the home hub (
llama3.1:8bby default) — the description of your mother's morning never leaves her house - Amazon Bedrock, if the household has cloud configured
- the deterministic sentence, which is never wrong
Every draft has to clear a fact gate before it is allowed near a family:
The last sign of life was at 08:16. ← pre-written; the model must copy it verbatim
06:44, 08:16, 11:00 ← the only clock times allowed to appear
This is not theoretical. During development the local model turned "14 days learned" into
"last sign of life was 14 days ago" — in a message to a worried family. The gate rejects
that draft and the rules version ships instead; tests/summary.test.ts keeps that exact
sentence as a regression test.
OLLAMA_MODEL=llama3.1:8b # optional, this is the default
AWS_REGION=us-east-1 # optional cloud path
BEDROCK_MODEL_ID=anthropic.claude-3-5-sonnet-20241022-v2:0app/
api/webhook/ring/ signature verification → parse → engine
api/household/ everything the dashboard polls
api/sim/ replay a scenario through the signed webhook path
api/live/ switch to a real Ring account
page.tsx the caregiver dashboard
lib/
ring/ API client, webhook types, HMAC verification
rhythm/ rhythm model + watch engine (pure, tested)
sim/ synthetic household: 14 days of history + four scenarios
notify/ family message (rules, optionally rephrased by Bedrock)
scripts/ring-probe.ts inspect a real account from the terminal
MIT — see LICENSE.
