Files
2026-07-11 21:51:34 -05:00

3.0 KiB

Mirror OS

Smart mirror app platform built with Flutter for a hacked Lululemon Mirror (BOE panel + Vizio driver board + Raspberry Pi 4).

Architecture

Melos mono-repo with two apps sharing packages:

  • apps/display — Flutter Linux desktop app, runs fullscreen on Pi 4 (1080x1920 portrait)
  • apps/remote — Flutter mobile app (iOS/Android), phone remote control
  • packages/mirror_protocol — WebSocket message types and serialization
  • packages/mirror_core — Shared business logic, API clients (workout-player, YouTube)
  • packages/mirror_ui — Dark theme, shared widgets, animations
  • services/ — Python sidecars for voice (OpenWakeWord + Whisper) and motion (PIR/GPIO)
  • deploy/ — Pi setup scripts, systemd units, labwc compositor config

Prerequisites

  • Flutter SDK 3.24+ (stable channel)
  • Dart SDK 3.5+
  • Melos: dart pub global activate melos

Setup

melos bootstrap

Development

# Run display app (macOS/Linux desktop for development)
cd apps/display && flutter run -d macos

# Run remote app on iOS simulator
cd apps/remote && flutter run

# Run both and test WebSocket communication:
# 1. Start display app (starts WS server on :9090)
# 2. Start remote app, connect to localhost:9090

# Analyze all packages
melos run analyze

# Format all packages
melos run format

Hardware

  • Panel: BOE DV430FHM-NN5, 43" 1080p IPS, portrait orientation (1080x1920)
  • Driver board: Vizio mainboard (HDMI passthrough → LVDS → panel)
  • Compute: Raspberry Pi 4 (4GB)
  • Control: Phone over same WiFi network
  • Rotation: Pi handles via wlr-randr --output HDMI-A-1 --transform 90

Communication

Display app runs a WebSocket server on port 9090. Phone remote connects as a client.

Protocol: JSON messages, typed via sealed Dart classes in mirror_protocol package.

  • Server → Client: StateSync (full state on connect), StatePatch (deltas)
  • Client → Server: NavigateCommand, PlaybackCommand, WorkoutCommand

External Services

  • Workout Player: http://192.168.86.172:8091 — REST API for video library, streaming, progress
  • OpenWeather: API key needed for dashboard weather widget
  • Google Calendar: OAuth for dashboard calendar widget
  • Plex: Local server on same LAN

Build for Pi 4

# On the Pi (with Flutter installed):
cd apps/display
flutter build linux --release

# Output: build/linux/arm64/release/bundle/
# Deploy: rsync bundle to /opt/mirror-os/display/ on the Pi

Testing

# Unit tests
melos exec -- dart test

# Integration: start display app, connect remote, verify state sync

Key Design Decisions

  • Riverpod for state management (no code generation needed for basic use)
  • Single DisplayState as source of truth on the Pi, broadcast to all remotes
  • media_kit (mpv) for media playback with V4L2 hardware decoding
  • Python sidecars for voice/motion (communicate via Unix domain sockets)
  • No touch input on mirror — all interaction via phone remote