Repository navigation
22 Home Camera Architecture
The Home Activity Camera is an optional, local-first add-on that turns a home RTSP, ONVIF or HTTP camera into suggested activity events for Daily Health (internal code name: BabyCare). A worker process on a machine inside the home network reads the stream, runs a coarse motion and presence analyser, and sends only derived metadata (kind, time window, confidence, a few ratios) to the API. By default raw video stays on the home network. Every event starts as ai_observed and counts for nothing until a caregiver confirms or corrects it.
- Store camera configuration with the stream URL and credentials application-encrypted, and never return them to the app.
- Allow private-network stream addresses by default, and strip credentials embedded in URLs.
- Authenticate the local worker with a per-camera key. Only the key's hash is stored.
- Accept derived events only, deduplicated and filtered by analysis mode and enabled state.
- Provide review (confirm, correct, dismiss), add-to-journal and analytics that use parent-confirmed events only.
- Offer privacy controls: instant disable, retention setting, purge recordings, delete camera.
| Endpoint | Auth | Purpose |
|---|---|---|
GET/POST /api/patients/{pid}/cameras |
user (add: primary guardian) | list (redacted) and privacy notes, or add a camera |
PUT/PATCH/DELETE /api/cameras/{cid} |
user (PUT/DELETE: primary guardian) | edit, toggle settings, delete |
POST /api/cameras/{cid}/disable |
user | instant off switch |
POST /api/cameras/{cid}/test |
user | RTSP OPTIONS/DESCRIBE probe (Basic/Digest) or HTTP HEAD
|
POST /api/cameras/{cid}/worker-key |
primary guardian | issue or rotate the worker key, shown once |
GET /api/cameras/{cid}/preview |
user | one JPEG frame, only if OpenCV is installed on the API host |
GET /api/cameras/{cid}/worker-config |
X-Camera-Worker-Key |
stream URL and credentials for the worker only |
POST /api/cameras/{cid}/worker/ingest |
X-Camera-Worker-Key |
heartbeat, state and up to 500 derived events |
GET /api/patients/{pid}/camera-observations |
user | events with AI OBSERVED / PARENT CONFIRMED / PARENT CORRECTED / DISMISSED labels |
POST /api/camera-observations/{oid}/review |
user | confirm, correct (with corrected_kind) or dismiss |
POST /api/camera-observations/{oid}/add-to-journal |
user | create an activity or sleep entry with source=camera_confirmed
|
GET /api/patients/{pid}/camera-analytics |
user | confirmed minutes by day, pending count |
POST /api/cameras/{cid}/purge-recordings |
user | delete stored clip blobs |
flowchart LR
subgraph HOME["Home network"]
CAM["Local RTSP or ONVIF camera"] -->|"stream read locally"| WK["Local worker<br/>api.camera_worker"]
WK --> INF["Activity and movement inference<br/>MotionAnalyser on 64x36 grayscale"]
INF --> META["Derived metadata<br/>kind, window, confidence, ratios"]
RAW["Raw video and frames<br/>stay local by default"]
WK -.-> RAW
end
META -->|"HTTPS with worker key"| API["FastAPI<br/>worker ingest"]
API --> OBS["Camera observation<br/>review_status ai_observed"]
OBS --> REV{"Caregiver review"}
REV -->|"confirm or correct"| CONF["Confirmed observation"]
REV -->|"dismiss"| DIS["Dismissed<br/>hidden from timeline"]
CONF -->|"add to journal"| REC["Confirmed record<br/>activity or sleep entry<br/>source camera_confirmed"]
REC --> DH["Daily Health timeline<br/>and calendar"]
python -m api.camera_worker --api <url> --camera <id> --key <key> (or BABYCARE_API_URL, BABYCARE_CAMERA_ID, BABYCARE_CAMERA_KEY) runs on a home machine. The worker:
- Fetches
worker-configwith its key. If the camera is disabled or the analysis mode isoff, it reportsdisconnectedand idles, checking again every 30 s. - Opens the stream with
opencv-python-headless(required for real cameras) or--simulate Nsynthetic frames, and downsamples each frame to 64×36 grayscale. - Feeds frames to
MotionAnalyserand sends closed segments plus a heartbeat at least every 60 s. - Stops when the ingest response says
enabled: false. On errors it reportsreconnectingand retries with exponential back-off up to 120 s. - Refuses a non-local API that is not
https://.
camera.new_worker_key() makes a token_urlsafe(32) key. Only sha256(key) is kept in worker_key_hash. Issuing a new key invalidates the old one. worker_state takes the values never_connected, connected, reconnecting, disconnected and disabled.
stream_url, username, password, name and room are EncryptedText columns. split_credentials() moves any user:pass@ in the URL into separate fields. API responses carry only stream_display (via redact(), which removes credentials and query strings) and a has_credentials flag. Credentials are returned in clear only by worker-config, and only to the holder of the worker key. Edit snapshots in record_revisions exclude the URL and credentials.
| Mode | Effect |
|---|---|
off |
ingest accepts no events |
motion (default) |
MotionAnalyser events are accepted |
activity |
stored and returned to the worker. The bundled worker runs the same MotionAnalyser
|
MotionAnalyser emits movement (motion ratio ≥ 0.025) and low_activity (≤ 0.004) segments of at least 20 s (5 s in simulation). Confidence is 0.4 + 0.4·signal strength + 0.2·min(1, length/300 s), so it always lies between 0.4 and 1.0. Ingest accepts the full OBSERVATION_KINDS vocabulary (movement, active, low_activity, presence, no_presence, rest, standing, sitting, walking_like, crawling_like). Posture classes with confidence below 0.6 are skipped, and duplicates are dropped by (camera_id, dedupe_key).
ai_observed → parent_confirmed | parent_corrected (with corrected_kind and note) | dismissed. Add-to-journal creates an ActivityEntry or SleepEntry (rest is suggested as sleep) with client_key=camera-{id}, so it cannot be added twice (409), and confirms an ai_observed event. The month calendar counts only parent-confirmed events. camera-analytics sums minutes from confirmed and corrected events and reports AI-only events separately as pending.
| Setting | Default |
|---|---|
enabled |
false |
local_network_only |
true: a non-private host returns 422 unless explicitly allowed |
audio_enabled |
false. The worker never captures audio |
record_clips |
false |
cloud_upload_allowed |
false |
retention_days |
7 (0–90) |
Disabling sets worker_state=disabled, after which ingest accepts nothing. Deleting a camera deletes its clip blobs and its observations (cascade). The preview endpoint is the one path where a frame passes through the API host: it is on-demand, authenticated, and only works where that host has OpenCV and can reach the camera.
The ingest schema is a closed Literal list, so a fever event is rejected with 422. The camera never creates symptom, fever, medicine or stool records (test_camera_api_hides_credentials_and_handles_review_states checks that symptoms stays empty). The UI and API warnings state that events describe movement and presence only and are "never used to infer fever, illness, pain or development". walking_like and crawling_like need a pose model that this build does not ship, so the bundled analyser never emits them.
api/camera.py, api/camera_api.py, api/camera_worker.py, api/care_models.py (Camera, CameraObservation), api/care.py (timeline and calendar integration), mobile/CameraScreen.tsx, web/care.js, tests/test_camera.py.
Daily Health and Care Calendar · Encryption and Private Storage · Security and Privacy Model · Design Decisions · Limitations · Roadmap
Overview
Platform
Identity and access
Medical records
- 10 Document Ingestion
- 11 OCR and Human Verification
- 13 Medicines and Administration
- 14 Vaccination Model
- 15 Labs, Growth, Longitudinal
- 16 Illness and Lifetime Health
Daily care
Output and sharing
Security
Engineering
- 26 Testing and Validation
- 27 Deployment and CI/CD
- 28 Technology Stack
- 29 Design Decisions
- 30 Limitations
- 31 Roadmap