# Spectra Display random Unsplash photos on an Inky Impression e-paper display, with a web interface for uploading your own images. --- - [Requirements](#requirements) - [Quick start](#quick-start) - [Configuration](#configuration) - [Display orientation](#display-orientation) - [MQTT](#mqtt) - [Usage](#usage) - [Display loop](#display-loop) - [Web interface](#web-interface) - [systemd services](#systemd-services) - [Running tests](#running-tests) - [Project structure](#project-structure) --- ## Requirements - Raspberry Pi with GPIO - [Pimoroni Inky Impression](https://shop.pimoroni.com/products/inky-impression) (4.0", 7.3", or 13.3") - Python 3.9+ - [Unsplash API access key](https://unsplash.com/developers) ## Quick start ```bash # Install cp config.example.yaml config.yaml # edit with your Unsplash key pip install -r requirements.txt pip install -e . # Run once in simulation mode spectra --once --simulate # Start the web interface spectra web # Run the display loop continuously spectra ``` Or on a Pi with systemd: ```bash sudo ./install.sh sudo systemctl start spectra spectra-web ``` ## Configuration spectra looks for `config.yaml` in this order: 1. `$PWD/config.yaml` 2. `~/.config/spectra/config.yaml` 3. `/etc/spectra/config.yaml` Pass a custom path with `spectra -c /path/to/config.yaml`. ```yaml unsplash: access_key: "your_access_key_here" query: "nature" collections: "" display: saturation: 0.5 orientation: 0 resolution: width: 1600 height: 1200 schedule: interval_hours: 1 random_delay_seconds: 300 mqtt: enabled: false broker: localhost port: 1883 topic_prefix: spectra client_id: spectra-display username: "" password: "" paths: cache: /var/cache/spectra ``` ### Display orientation Set `display.orientation` to match how the panel is physically mounted. The Unsplash orientation filter (`landscape`/`portrait`/`squarish`) is derived automatically — the old `unsplash.orientation` config key is ignored. | Value | Unsplash filter | Effect | |-------|----------------|--------| | 0 | landscape | Default horizontal mount | | 90 | portrait | Vertical mount | | 180 | landscape | Upside-down horizontal | | 270 | portrait | Upside-down vertical | Images are cropped to the effective aspect ratio, resized, then rotated for the physical panel. Preview and thumbnails reflect the configured orientation. Change it live from Settings > Display Orientation or via config hot-reload. ### MQTT When enabled, the display loop subscribes to `{prefix}/command/#` and publishes status to `{prefix}/status`. See [MQTT integration docs](docs/mqtt.md) for commands, Home Assistant examples, and debugging. ## Usage ### Display loop ```bash spectra # run continuously spectra --once # fetch one photo and exit spectra --once --simulate # save to /tmp/spectra_last.png spectra --simulate --width 800 --height 480 spectra -c /path/to/config.yaml ``` ### Web interface ```bash spectra web # http://0.0.0.0:5000 spectra web --port 8080 spectra web -v # verbose (debug mode only on localhost) ``` | Page | What it does | |------|-------------| | **Dashboard** | Display status, schedule, quick actions (refresh, clear) | | **Gallery** | Browse images, trigger "show now", delete | | **Upload** | Drag-and-drop multi-file upload with progress | | **Settings** | Live-edit Unsplash, display, schedule, MQTT | | **Preview** | See how an image looks on the display | ## systemd services Two units are installed by `install.sh`: - `spectra.service` — display loop - `spectra-web.service` — web interface ```bash sudo systemctl start spectra sudo systemctl stop spectra-web journalctl -u spectra -f journalctl -u spectra-web -f ``` ## Running tests ```bash pip install -e ".[dev]" pytest tests/ ``` 107 tests covering config loading, display processing (crop, resize, rotation), trigger atomicity, MQTT parsing, and all 19 API routes. A [Gitea Actions](.gitea/workflows/test.yml) workflow runs tests on every push. See [bug-tracking.md](bug-tracking.md) for known issues and recent fixes. ## Project structure ``` spectra/ ├── config.example.yaml ├── .gitignore ├── install.sh ├── pyproject.toml ├── requirements.txt ├── spectra/ │ ├── cli.py # CLI, display loop, config hot-reload │ ├── config.py # Config loader with deep-merge │ ├── config_manager.py # Config read/write │ ├── display.py # Image processing, rotation, hardware abstraction │ ├── fetcher.py # Unsplash API client │ ├── library.py # Unsplash image caching to SQLite │ ├── mqtt.py # MQTT client (commands + heartbeat) │ ├── trigger.py # Thread-safe trigger file (web → display loop) │ └── web/ │ ├── server.py # Flask app, 19 API routes │ ├── models.py # SQLAlchemy models │ ├── templates/ # 6 Jinja2 pages │ └── static/ # CSS + JS ├── .gitea/ │ └── workflows/ │ └── test.yml # CI workflow ├── tests/ │ ├── test_api.py │ ├── test_config.py │ ├── test_display.py │ ├── test_gallery.py │ ├── test_mqtt.py │ ├── test_orientation.py │ ├── test_trigger.py │ └── conftest.py ├── systemd/ │ ├── spectra.service │ └── spectra-web.service └── docs/ ├── display-orientation.md ├── web-interface.md └── mqtt.md ```