99 lines
3.0 KiB
Markdown
99 lines
3.0 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
melos bootstrap
|
|
```
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|