product

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_intercom payload) — the wall shows a banner + plays the audio (reuse the wall's existing audio path; speechSynthesis for typed lines, or the owner's cloned voice via sayInUserVoice like 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 from allow_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

  1. #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.
  2. Intercom PR: screens list + online state → quiet hours → send + wall pickup/ack → tap-to-talk UI (dashboard + on-wall).
FAMILY WALL INTERCOM — Docs | HiveJournal