Files
spectra/README.md
2026-06-25 21:20:00 -06:00

7.2 KiB

Spectra

Display random Unsplash photos on an Inky Impression e-paper display, with a web interface for uploading your own images.

Features

  • Fetches random high-resolution photos from Unsplash (cached to gallery)
  • Upload your own images via the web interface (drag-and-drop, multi-file)
  • Centres, scales, and rotates images to fit the display
  • Colour saturation tuning for e-paper
  • Display orientation — configure the physical mounting direction (0°, 90°, 180°, 270°) — Unsplash orientation filter is auto-derived (portrait/landscape/squarish)
  • Web dashboard with preview, gallery, rotation queue, and config editor
  • MQTT integration for remote commands and status reporting
  • Simulation mode for testing without hardware (configurable resolution)
  • systemd integration for automatic updates on a schedule
  • Randomised update intervals to avoid predictable refreshes
  • Config hot-reload — no service restart needed for setting changes

Requirements

Installation

Run the installer on a Raspberry Pi:

./install.sh

The installer will:

  1. Install the Pimoroni Inky library (skipped if not a Raspberry Pi)
  2. Install Python dependencies (including web server and MQTT)
  3. Install the spectra package
  4. Copy the default config to /etc/spectra/config.yaml
  5. Install and enable both the display and web interface systemd services

Manual installation

pip install -r requirements.txt
pip install -e .

# Optional: install dev dependencies for running tests
pip install -e ".[dev]"

Configuration

Copy config.example.yaml to config.yaml and fill in your Unsplash access key. spectra searches for config.yaml in this order:

  1. $PWD/config.yaml
  2. ~/.config/spectra/config.yaml
  3. /etc/spectra/config.yaml

Or pass a custom path: spectra -c /path/to/config.yaml

unsplash:
  access_key: "your_access_key_here"
  query: "nature"
  collections: ""

display:
  saturation: 0.5
  orientation: 0       # 0, 90, 180, 270 (degrees clockwise)
  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

Note: The unsplash.orientation key is no longer used. The Unsplash orientation filter (landscape/portrait/squarish) is now derived automatically from display.orientation + display.resolution.

Usage

Display loop

# Run once and exit
spectra --once

# Run once in simulation mode
spectra --once --simulate

# Simulate at a specific resolution
spectra --once --simulate --width 800 --height 480

# Run with a custom config
spectra -c /path/to/config.yaml

# Run continuously
spectra

Web interface

# Start the web interface (default: http://0.0.0.0:5000)
spectra web

# With custom host/port
spectra web --host 0.0.0.0 --port 5000

# Verbose logging (debug mode is limited to localhost)
spectra web -v

The web interface provides:

  • Dashboard — display status, schedule info, quick actions (refresh, clear)
  • Gallery — browse all uploaded and cached Unsplash images, trigger "show now"
  • Upload — drag-and-drop multiple image files with progress tracking
  • Settings — live-edit Unsplash, display (including orientation), schedule, and MQTT configuration
  • Preview — preview how an image will look on the display (respects current orientation)

Display orientation

Set display.orientation in config or via Settings > Display > Display Orientation:

Value Unsplash filter Use case
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 to match the physical panel. The preview and thumbnails reflect the configured orientation.

MQTT

When MQTT is enabled in the config, the display loop subscribes to commands and publishes status. See MQTT integration docs for details.

systemd services

Two systemd services are installed:

  • spectra.service — the display loop (fetches Unsplash, processes triggers)
  • spectra-web.service — the web interface (Flask + htmx)
sudo systemctl start spectra
sudo systemctl stop spectra-web
sudo systemctl status spectra-web

# View logs
journalctl -u spectra -f
journalctl -u spectra-web -f

Running tests

pip install -e ".[dev]"
pytest tests/

107 tests covering config loading, display processing (crop, resize, rotation), trigger atomicity, MQTT parsing, and the full web API (all 19 routes).

Project structure

spectra/
├── config.example.yaml       # Example config (copy to config.yaml)
├── .gitignore                # Ignores config.yaml (contains secrets)
├── install.sh                # Installer script
├── pyproject.toml            # Package metadata
├── requirements.txt          # Python dependencies
├── spectra/
│   ├── __init__.py
│   ├── __main__.py           # python -m spectra entry point
│   ├── cli.py                # CLI argument parsing and main loop
│   ├── config.py             # Configuration loader
│   ├── config_manager.py     # Config read/write with YAML write-back
│   ├── display.py            # Inky display abstraction and image processing
│   ├── fetcher.py            # Unsplash API client
│   ├── library.py            # Unsplash image caching to SQLite library
│   ├── mqtt.py               # MQTT client (commands + status)
│   ├── trigger.py            # Shared trigger file (web server → display loop)
│   └── web/
│       ├── server.py         # Flask app factory and API routes
│       ├── models.py         # SQLAlchemy models
│       ├── templates/        # Jinja2 templates (6 pages)
│       ├── static/           # CSS and JS
│       └── __init__.py
├── tests/
│   ├── test_api.py           # 42 API endpoint tests
│   ├── test_config.py        # Config loading and deep-merge
│   ├── test_display.py       # Display processing and orientation
│   ├── test_gallery.py       # Upload + delete flow
│   ├── test_mqtt.py          # MQTT path and lookup
│   ├── test_orientation.py   # Orientation edge cases
│   ├── test_trigger.py       # Trigger atomicity
│   └── conftest.py           # Test fixtures
├── systemd/
│   ├── spectra.service       # Display loop systemd unit
│   └── spectra-web.service   # Web interface systemd unit
└── docs/
    ├── display-orientation.md # Display orientation design doc
    ├── web-interface.md      # Web interface documentation
    └── mqtt.md               # MQTT integration documentation