Skip to content

ExcuseMi/trmnl-image-webhook

Repository files navigation

TRMNL Image Webhook

Automatically upload images from your photo collection to your TRMNL e-ink display with proper dithering for beautiful grayscale rendering.

Example output

beach-2bit-dither-800x480.png

Features

  • 📸 Automatic Image Processing - Scales and converts photos to e-ink format
  • 🎨 Floyd-Steinberg Dithering - Professional halftone effect for smooth gradients
  • 🖼️ 2-bit Grayscale - 4 shades of gray for better quality than pure black & white
  • Gamma Correction - Brightens midtones for better e-ink visibility
  • 🖼️ Frame Borders - Optional decorative borders (line or rounded corners)
  • 🎨 Color Output Mode - Full RGB for Pimoroni Inky or similar color e-ink displays
  • 🎲 Multiple Selection Modes - Random, sequential, shuffle, newest, or oldest
  • 🔄 Auto-Rotation - Respects EXIF orientation data
  • 🖼️ Flexible Layouts - Auto, landscape, or portrait modes
  • 🎯 Orientation Filtering - Show only landscape or portrait photos (with smart caching)
  • 🎨 Border Styles - White, black, or blurred borders
  • 📏 Adjustable Margins - Add spacing around images for framed picture look
  • 📁 Subfolder Support - Organize photos in folders and subfolders
  • 🏷️ Optional Labels - Show filename or path on images
  • 🐳 Docker Ready - Easy deployment with docker-compose
  • 💾 State Management - Remembers position for sequential/shuffle modes
  • 🔍 Debug Mode - Saves processed images for inspection
  • 🧪 Dry Run - Test without uploading
  • 🔔 Version Checking - Notifies when updates are available

Quick Start

1. Get Your Webhook URL

  1. Log into TRMNL
  2. Go to Plugins > Webhook Image
  3. Click "Add to my plugins"
  4. Copy your webhook URL

2. Set Up Configuration

# Clone or download this repository
cd trmnl-image-webhook

# Copy example config
cp .env.example .env

# Edit with your settings
nano .env

Minimum required config:

WEBHOOK_URL=https://usetrmnl.com/api/plugin_settings/YOUR-UUID/image
IMAGES_PATH=/path/to/your/photos

3. Run with Docker

docker-compose up -d

That's it! Your TRMNL will start showing photos from your collection.

Configuration

Required Settings

Variable Description Example
WEBHOOK_URL Your TRMNL webhook URL https://usetrmnl.com/api/...
IMAGES_PATH Path to your photos ./images or /home/user/Photos

Optional Settings

Variable Default Description
BIT_DEPTH 2 Bit depth: 1 = black/white, 2 = 4 grays (recommended)
DISPLAY_WIDTH 800 Display width (OG: 800)
DISPLAY_HEIGHT 480 Display height (OG: 480)
LAYOUT auto Orientation: auto/landscape/portrait
ORIENTATION_FILTER any Filter images: any/landscape/portrait
BORDER_STYLE white Border style: white/black/blur
MARGIN 0 Margin in pixels (0-100) for framed look
GAMMA 1.5 Gamma correction (1.0-2.0, brightens midtones for e-ink)
FRAME_BORDER none Frame border: none/line/rounded (only with MARGIN > 0)
FRAME_BORDER_WIDTH 2 Frame border width in pixels (1-10)
OUTPUT_MODE grayscale Output: grayscale (TRMNL) or color (Pimoroni Inky)
INTERVAL_MINUTES 60 Minutes between uploads
SELECTION_MODE random How to pick images (see below)
INCLUDE_SUBFOLDERS true Include images from subdirectories
USE_DITHERING true Apply Floyd-Steinberg dithering
IMAGE_LABEL none Show label (none/filename/path)
DRY_RUN false Test mode - don't upload

Selection Modes

  • random - Pick any image randomly
  • sequential - Go through images A-Z, remembers position
  • shuffle - Random order, each image once before reshuffling
  • newest - Always show most recently modified image
  • oldest - Show oldest image first

Image Labels

Add text overlay to images:

  • none - No label (default)
  • filename - Show just the filename
  • path - Show relative path from images directory

Example with label:

IMAGE_LABEL=path

Gamma Correction

Brightens midtones for better e-ink visibility. E-ink displays tend to look darker than regular screens, so gamma correction helps.

GAMMA=1.5  # Recommended for e-ink (default)
GAMMA=1.0  # No correction (original brightness)
GAMMA=1.8  # More aggressive brightening

Frame Borders

Add decorative borders around images for a framed picture look. Requires MARGIN > 0.

MARGIN=40              # Space around image
FRAME_BORDER=line      # Style: none/line/rounded
FRAME_BORDER_WIDTH=3   # Border thickness (1-10 pixels)
BORDER_STYLE=white     # Background: white/black

Examples:

  • Gallery style: MARGIN=60, FRAME_BORDER=line, BORDER_STYLE=black
  • Modern: MARGIN=30, FRAME_BORDER=rounded, BORDER_STYLE=white
  • Classic: MARGIN=50, FRAME_BORDER=line, FRAME_BORDER_WIDTH=5

Color Output Mode

For Pimoroni Inky or other color e-ink displays. Outputs full RGB instead of dithered grayscale.

OUTPUT_MODE=color                              # Enable color mode
WEBHOOK_URL=http://192.168.1.x:5000/display  # Your local endpoint

Note: Color mode produces larger files (200-500KB) and is not compatible with TRMNL's cloud service. Use for local Pimoroni Inky setups only.

Image Fit Modes

Control how images are scaled to the display:

Contain (default):

IMAGE_FIT=contain
  • Fits entire image on screen
  • Maintains aspect ratio
  • May show borders if image ratio doesn't match display
  • Best for preserving full image

Fill:

IMAGE_FIT=fill
  • Fills entire display
  • Maintains aspect ratio
  • May crop edges of image
  • Best for edge-to-edge display, no borders
  • Great for wallpaper-style images

Example - Full Screen Photos:

IMAGE_FIT=fill
MARGIN=0
BORDER_STYLE=white

Image Processing

What Happens to Your Photos

  1. Scaling - Resized to fit display (800x480 or 1280x800)
  2. Grayscale - Converted to grayscale
  3. Dithering - Floyd-Steinberg dithering applied for smooth gradients
  4. 1-bit Conversion - Pure black and white (2 colors)
  5. PNG Export - Optimized 1-bit PNG (~20-40KB)

Why Dithering?

TRMNL displays are 1-bit (pure black and white). Dithering creates the illusion of grayscale by using patterns of black and white dots - like newspaper photos. This makes photos look much better than simple thresholding.

With dithering:

  • Smooth gradients in sky, skin tones, etc.
  • Details visible in shadows and highlights
  • Professional halftone appearance

Without dithering:

  • Harsh black/white contrast
  • Loss of detail
  • Posterized look

Examples

Basic Setup

# .env
WEBHOOK_URL=https://usetrmnl.com/api/plugin_settings/abc-123/image
IMAGES_PATH=/home/user/Photos
INTERVAL_MINUTES=60
SELECTION_MODE=random
INCLUDE_SUBFOLDERS=true
USE_DITHERING=true

Photo Album (Sequential)

SELECTION_MODE=sequential
INTERVAL_MINUTES=120
IMAGE_LABEL=filename

Slideshow (Shuffle)

SELECTION_MODE=shuffle
INTERVAL_MINUTES=30
INCLUDE_SUBFOLDERS=true

Latest Photo Display

SELECTION_MODE=newest
INTERVAL_MINUTES=15
IMAGE_LABEL=path

Framed Picture Gallery

MARGIN=40
BORDER_STYLE=white
FRAME_BORDER=line
FRAME_BORDER_WIDTH=3
GAMMA=1.5

Classic Black Frame

MARGIN=60
BORDER_STYLE=black
FRAME_BORDER=rounded
FRAME_BORDER_WIDTH=2

Pimoroni Inky Color Display

OUTPUT_MODE=color
DISPLAY_WIDTH=800
DISPLAY_HEIGHT=480
GAMMA=1.5
MARGIN=30
FRAME_BORDER=line
WEBHOOK_URL=http://192.168.1.x:5000/display  # Your local Pi endpoint

Deployment

Docker Compose (Recommended)

# Start
docker-compose up -d

# View logs
docker-compose logs -f  trmnl-image-webhook

# Stop
docker-compose down

# Restart after config changes
docker-compose restart

Synology NAS

  1. Enable Docker in Package Center
  2. Upload project folder to your NAS
  3. Edit .env with your settings
  4. SSH into NAS:
cd /volume1/docker/trmnl-image-webhook
docker-compose up -d

Raspberry Pi

# Install Docker
curl -fsSL https://get.docker.com | sh

# Clone and configure
git clone <repo-url>
cd trmnl-image-webhook
cp .env.example .env
nano .env

# Run
docker-compose up -d

Debugging

Check Logs

docker-compose logs -f

Look for:

Found X images
Converting to grayscale
Converting to 1-bit with Floyd-Steinberg dithering  
Final: 800x480 1-bit PNG, 25.3KB
✓ Successfully uploaded image.jpg
Response: 200

Inspect Processed Images

Every upload saves two files to ./data/:

  • last_original.jpg - Original unprocessed photo
  • last_processed.png - Final 1-bit dithered PNG sent to TRMNL

Compare these to see exactly what's being displayed.

Dry Run Mode

Test without uploading:

DRY_RUN=true
docker-compose restart

Common Issues

No images found

  • Check IMAGES_PATH is correct
  • Set INCLUDE_SUBFOLDERS=true if images are in subdirectories
  • Verify image formats (PNG, JPG, JPEG, BMP, GIF supported)

Images not displaying on TRMNL

  • Check device WiFi connection
  • Verify webhook URL is correct
  • Try "Force Refresh" in TRMNL plugin settings
  • Check data/last_processed.png looks correct

Rate limited (429 error)

  • TRMNL allows max 12 uploads per hour
  • Increase INTERVAL_MINUTES to 60 or higher

Technical Details

Supported Image Formats

Input: PNG, JPEG, JPG, BMP, GIF
Output: 1-bit or 2-bit grayscale PNG (default: 2-bit)

Bit Depth Options

2-bit (Default - Recommended):

  • 4 shades of gray (0, 85, 170, 255)
  • Smoother gradients and better photo quality
  • Smaller file sizes (~5-10KB)
  • Supported on TRMNL OG firmware v1.7.2+

1-bit (Classic):

  • Pure black and white (2 colors)
  • Sharper, higher contrast
  • Slightly larger files (~15-30KB)
  • Maximum compatibility

Both modes use Floyd-Steinberg dithering for professional halftone effects.

File Sizes

  • Input: Any size (auto-scaled)
  • Output: ~15-40KB (1-bit PNG)
  • Limit: 5MB (rarely reached)

Display Sizes

TRMNL OG:

DISPLAY_WIDTH=800
DISPLAY_HEIGHT=480

State Management

For sequential and shuffle modes, position is saved in ./data/state.json:

{
  "last_image": "vacation/photo.jpg",
  "current_index": 42,
  "last_upload": "2024-12-31T16:20:57"
}

Performance

  • Memory: ~50MB
  • CPU: Minimal (only during processing)
  • Network: ~20-40KB per upload
  • Storage: State file <1KB

Requirements

  • Docker & Docker Compose
  • Network access to TRMNL API
  • Directory of images (local or mounted)

License

MIT License - see LICENSE file

Contributing

Issues and pull requests welcome!

Support

For issues with:

  • This tool: Open a GitHub issue
  • TRMNL device/service: Contact TRMNL support
  • Docker/deployment: Check Docker logs first

Credits

Developed for the TRMNL community. Inspired by TRMNL's own image processing code.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages