Files
Kareem Horstink f60e7b5ad1
Build Server .exe / build (push) Failing after 2m30s
Switch from venv to conda for environment management
- start.bat and start.sh now create conda env (paperdash, Python 3.11)
- README updated with conda setup instructions
- Fixes pyhwinfo installation issues on Python 3.12+
2026-07-03 08:38:34 +00:00

260 lines
9.3 KiB
Markdown

# E-INK Dashboard Ecosystem ~ Paperdash
A local network dashboard ecosystem designed for the **Onyx Boox Go 7 Color** (Android E-Ink display). It displays real-time Windows desktop states including media playback, audio mixer channels (Windows default output or Voicemeeter), system telemetry, and custom script notifications (progress bars and rich text alerts).
**Copyright (c) Imrayya (2026)** — MIT License
## Architecture
Paperdash bypasses heavyweight native Android UIs in favor of a lightweight, asynchronous Python server communicating via low-overhead WebSockets to a highly optimized E-Ink web client.
```mermaid
flowchart TD
subgraph WindowsHost["WINDOWS HOST PC"]
subgraph Subsystems["Subsystems"]
Media["WinRT Media API"]
Audio["Windows Audio / Core Audio"]
VM["Voicemeeter API"]
HW["HWiNFO Shared Memory"]
end
Server["FastAPI Server"]
StateAgg["State Aggregator"]
Auth["Token Auth Engine"]
end
subgraph External["EXTERNAL CLIENTS"]
Script["Custom Script / CLI"]
end
subgraph Boox["ONYX BOOX GO 7 COLOR (Android 13)"]
APK["Android WebView APK
(Kotlin)"]
style APK fill:#000,color:#fff
end
Media --> Server
Audio --> Server
VM --> Server
HW --> Server
Server --> StateAgg
Server --> Auth
Script -->|Bearer Token| Auth
StateAgg -->|WebSocket| WebView
Auth -->|PIN Auth| WebView
```
**Client-side optimizations:** PIN-authenticated WebSocket connection, static DOM updates (no smooth scrolling/fades), full-screen CSS Grid layout with static tap-and-swipe gestures.
## Tech Stack
### Server (Python 3.11+, Windows)
- **FastAPI + Uvicorn** — ASGI web framework with native async WebSocket handling
- **winsdk** — Windows Media API for GSMTC (Global System Media Transport Controls)
- **pysounddevice / pycaw** — Generic Windows Core Audio API for default output volume and mute
- **voicemeeter-api** — Voicemeeter Remote API wrapper for per-strip mixer controls
- **pyhwinfo** — Hardware telemetry via HWiNFO64 Shared Memory
- **PyInstaller** — Compiles the server to a standalone `.exe` with system tray icon
### Client (BOOX Go 7 Color, Android 13)
- **Custom Android WebView APK** — Kotlin app wrapping the web client in a full-screen WebView
- **Vanilla JavaScript (ES6+) + CSS** — configured for monochrome/high-contrast E-Ink palettes
## Features
- **Media Playback Tracking** — Song title, artist, playback percentage, and cover art via Windows GSMTC
- **Audio Mixer Control** — Real-time volume and mute from Windows default output or Voicemeeter per-strip gains, mutes, and track states
- **Hardware Telemetry** — CPU/GPU core temps, VRAM usage from HWiNFO64
- **Hardware Fallbacks** — Startup check reports which subsystems are available/unavailable
- **Custom Notifications** — External scripts can push progress bars or rich text alerts via `POST /api/v1/notify`
- **Settings Page** — Localhost-only web UI at `/settings` (accessed via tray icon) to edit PIN, token, host, port, log level, and PIN TTL
- **Secure PIN System** — 6-digit codes with 5-minute TTL, single-use, auto-generated on startup
- **Cryptographic API Token** — 256-bit entropy via `secrets.token_urlsafe(32)`
- **Auto-Configuration** — `config.yaml` created automatically on first run with detected local IP
- **Configuration Files** — Use `config.yaml`, `.env`, or environment variables
- **E-Ink Optimized UI** — High-contrast styling, discrete zone gestures, minimal refresh
- **System Tray .exe** — Runs unobtrusively in the Windows notification bar with Settings and Quit options
## Authentication
1. **Dashboard UI (Boox Tablet):** Time-limited PIN authentication via WebSocket handshake (`?pin=XXXX`). PINs are 6-digit, expire after 5 minutes (configurable), and can only be used once.
2. **External Scripts/CLI:** Cryptographic Bearer Token via `Authorization: Bearer <SECRET_TOKEN>` header (32 bytes / 256 bits of entropy)
## Project Structure
```
e-ink-dash/
├── .pi/
│ ├── docs/design/ # Design decision records
│ └── paperdash_architecture_document.txt
├── server/ # Python FastAPI server
│ ├── main.py # Application entry point
│ ├── config.py # Configuration management (YAML/.env/env vars)
│ ├── pin_manager.py # Time-limited single-use PIN generation
│ ├── audio_mixer.py # Audio mixer abstraction (Windows + Voicemeeter)
│ ├── media.py # Media playback tracking (GSMTC)
│ ├── notifications.py # Notification system (progress + alerts)
│ ├── telemetry.py # Hardware telemetry (HWiNFO)
│ ├── tray_wrapper.py # System tray .exe wrapper
│ ├── settings.html # Settings page (localhost-only)
│ ├── settings.css # Settings page stylesheet
│ ├── build_exe.py # PyInstaller build script
│ ├── start.bat # Windows development startup
│ ├── start.sh # Linux/macOS development startup
│ ├── .env.example # Example environment file
│ ├── requirements.txt
│ └── tests/ # Test suite
├── client/ # E-Ink web client
│ ├── index.html
│ ├── style.css
│ └── app.js
├── README.md
├── LICENSE
└── .gitignore
```
## Getting Started
### Quick Start (Development)
```bash
cd server
bash start.bat # Windows
# or
bash start.sh # Linux/macOS
```
The scripts create a conda env (`paperdash` with Python 3.11), install dependencies, and start the server. On first run, `config.yaml` is auto-generated with:
- A random 6-digit PIN (logged to console)
- A random API token (logged to console)
- Detected local IP address
- Default port 8921
### Manual Setup
```bash
cd server
conda create -y -n paperdash python=3.11
conda activate paperdash
pip install -r requirements.txt
python -m uvicorn main:app --host 0.0.0.0 --port 8921
```
Configure via one of:
1. **Environment variables:**
- `DASHBOARD_PIN` — (optional, auto-generated if missing)
- `API_TOKEN` — Bearer token for external scripts
- `PIN_TTL` — PIN expiration in seconds (default: 300)
- `HOST` — Server bind address (default: detected local IP)
- `PORT` — Server port (default: 8921)
- `LOG_LEVEL` — Logging level (default: INFO)
2. **`.env` file** (in project root or exe directory):
```
API_TOKEN=your-token-here
PIN_TTL=300
HOST=192.168.1.100
PORT=8921
LOG_LEVEL=INFO
```
3. **`config.yaml`** (auto-created on first run, or manually):
```yaml
api_token: "your-token-here"
pin_ttl: 300
host: "192.168.1.100"
port: 8921
log_level: "INFO"
```
### Settings Page
Access the settings UI via the system tray icon → **Settings**. This opens `http://127.0.0.1:8921/settings` in your browser.
**Available only from localhost** — you must be on the PC running PaperDash.
Edit: PIN, API token, host, port, log level, and PIN TTL. Changes are saved to `config.yaml` and take effect on next restart.
### Build .exe
```bash
cd server
python build_exe.py
# Output: server/dist/PaperDash.exe
```
The `.exe` bundles `settings.html` and `settings.css` for the settings page.
### Build .exe
```bash
cd server
python build_exe.py
# Output: server/dist/PaperDash.exe
```
### Client (Android APK)
The web client is bundled into an Android APK. Open Android Studio, sync Gradle, and run on the BOOX Go 7 Color.
```bash
cd android
./gradlew assembleDebug
# APK at: app/build/outputs/apk/debug/app-debug.apk
```
Install via ADB:
```bash
adb install app/build/outputs/apk/debug/app-debug.apk
```
The WebView loads `file:///android_asset/index.html` and connects to the server via WebSocket at `ws://<pc-ip>:8921/ws`.
#### Localization
The app supports multiple languages via Android string resources. The web client loads strings from Android via a JavaScript interface.
**Adding a new language:**
1. Create a new values folder: `android/app/src/main/res/values-{lang}/`
- Example: `values-es/` for Spanish, `values-fr/` for French
2. Copy `values/strings.xml` to the new folder
3. Translate the string values
**Example `values-es/strings.xml`:**
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PaperDash</string>
<string name="login_title">PaperDash</string>
<string name="login_subtitle">Ingrese PIN para conectar</string>
<string name="login_placeholder">****</string>
<string name="login_button">Conectar</string>
<string name="login_error_enter_pin">Ingrese un PIN</string>
<string name="login_error_disconnected">Desconectado. Reingrese PIN.</string>
<string name="connecting">Conectando…</string>
<string name="no_playback">Sin reproducción</string>
<string name="mute">SILENCIAR</string>
<string name="muted">SILENCIADO</string>
<string name="no_notifications">Sin notificaciones</string>
<string name="eta_prefix">ETA:</string>
</resources>
```
The app automatically uses the device's language. No code changes needed.
### Design Decisions
See `.pi/docs/design/` for detailed design decision records covering architecture, audio mixer, notifications, and the .exe build.
## License
MIT — see [LICENSE](LICENSE) for details.