Family Wall — intercom
Status: ✅ BUILT v1 (2026-07-18, migration 540) — see INDEX.md → "Family Wall
intercom". Typed messages ship; audio-clip recording (the audio_url
column) is the named follow-up. Shares the wall device identity foundation with
JQ voice actions (location_kind + owner_member_id).
The ask
Turn the family's wall screens into an intercom: from one wall (or the dashboard), see which screens are online, know whether each is a shared or a bedroom screen, see the avatars of who can receive there, respect quiet-time windows, and tap to talk to someone on a specific screen.
Pieces
1. Which screens are online
Reuse the existing liveness signal — a running wall polls
GET /api/family/wall/view/:token every 60s and we stamp
family_wall_displays.last_seen_at; "live now" = seen <90s ago. A new
GET /api/family/intercom/screens (auth → family owner) returns each wall with
{ name, location_kind, owner_member(avatar,name), online, last_seen_at }.
2. Shared vs. bedroom
From location_kind (see the shared foundation doc). Shared screens show all reachable
members; a personal bedroom screen shows just its owner_member. Drives quiet-hours
defaults (bedrooms quiet by default) and consent posture.
3. Avatars of who can receive
Per screen, the family-member avatars reachable there:
- personal wall → its
owner_member. - shared wall → members currently home (join with
family_presence), or all members as a fallback.
4. Quiet-time windows
New family_wall_quiet_hours (or two columns on the display: quiet_start,
quiet_end local time, nullable). During a screen's quiet window an intercom message
is held/muted (shown silently on wake, no audible ping) rather than played aloud.
Bedroom screens get a sensible default (e.g. 8pm–7am); the owner can edit. Quiet hours
are a hard rule for minor/bedroom screens, not a preference.
5. Tap-to-talk
Sender taps a screen (or a member's avatar on it) → records a short clip or types a line → we deliver it to that screen. Two delivery options, cheapest first:
- Text/quick clip via a per-wall inbox the wall polls (add to the existing 60s
view poll a
pending_intercompayload) — the wall shows a banner + plays the audio (reuse the wall's existing audio path;speechSynthesisfor typed lines, or the owner's cloned voice viasayInUserVoicelike the wall parent-message). - Later: near-real-time via SSE/websocket (no realtime infra on walls today — v1 is short-poll, which is fine for an intercom).
New: family_intercom_messages (id, parent_user_id, to_wall_id, to_member_id?,
from_label, body?, audio_url?, created_at, delivered_at?, played_at?).
Send: POST /api/family/intercom/send (auth). Wall pickup: fold pending_intercom
into the public wall/view/:token response (token-scoped); wall marks
played_at via a small public ack route (re-scoped to the wall's parent).
Trust / safety
- Sending is a new capability from a public surface → its own opt-in per wall
(
allow_intercom, default off), separate fromallow_checkin/allow_editing. - Everything re-scoped to the wall's own parent (a token can only reach that family's own screens). Quiet hours enforced server-side, not just client-side.
- Bedroom/minor screens inherit the [[project_family_frame]] counsel posture; an adult → child bedroom screen is the sensitive path — respect quiet hours + keep it parent-initiated.
Sequencing
- #1393 merges; JQ-actions PR lands the shared device identity columns
(
location_kind,owner_member_id) — or this PR lands them first if built first. - Intercom PR: screens list + online state → quiet hours → send + wall pickup/ack → tap-to-talk UI (dashboard + on-wall).